본문 바로가기
# 공부/# 그 외 흥미로운 문제들

리팩토링에 대해

by 쁘레레레레레 2024. 9. 1.

리팩토링에 대한 밀린 글은 좀 써볼까 한다.

 

# 목차

1. 리팩토링이란 무엇인가?

리팩토링은 소프트웨어 개발에서 기존 코드의 기능을 유지하면서 코드의 구조를 개선하는 프로세스를 말한다.

마틴 파울러(Martin Fowler)의 정의에 따르면, 리팩토링은 "외부 행동의 변화 없이 내부 구조를 개선하는 것"으로, 코드의 가독성, 유지보수성, 확장성을 높이는 데 중점을 둔다.

리팩토링은 소프트웨어 개발 주기에서 지속적으로 이루어져야 하는 중요한 작업이다.

 

일 다니면서 팀원 관리하고 프로젝트 관리에 일상생활까지 하려니 굉장히 빡세서 블로그 업데이트가 밀렸었는데...

 

몇달전 공부하며 시도했고 마무리 했던 리팩토링에 대해 다뤄본다.

 

2. 리팩토링의 필요성?

우리는 모두 목적을 가지고 코드를 작성한다.

"캐릭터의 정보를 저장할 클래스를 만들자"

"그리고 거기에 경험치를 얻을 때마다 불려질 함수, 그리고 레벨업하는 함수를 만드는거야"

"음.. 그리고 스탯을 계산할 코드는 어떄?"

 

사실 여기까지는 '캐릭터의 정보를 저장한다' 라는 의미에서 예상을 하고 있던 내용이다.

 

하지만, 이러면 어떨까?

 

"너무 진부해. 새로운 캐릭터가 나왔으면 좋겠어. 얘는 마법을 다루는데, 마나를 소모하지 않고 땅의 속성에 따라 달라지는거지"

"그런데, 주인공은 땅의 기운을 불러내는거야! 마치 메x플스토리에 라라처럼! 땅 마다 계수가 다르고 (어쩌고 저쩌고...)"

 

이러면 어떨까? 간단하다고?

 

이러한 업데이트가 쌓이고 복잡해지다보면 아마 본인이 예측했던 범위를 빗나갈 확률이 매우 크다. 실제로 저자도 그렇게 말했고, 나 역시 그랬었다.

 

그럼 이게 우리 의도대로 작동할까?  어쩌면 코드가 더 비효율적이 되어 맛있는 스파게티가 만들어질 수 있다. (갑자기?)

 

아무튼, 리팩토링은 그러한 코드들을 잘 정리해 복잡성을 줄이고 코드 베이스를 최신상태로 유지하여 유지보수를 용이하게 만든다는 장점이 있다.

 

3. 리팩토링의 기본 원칙

마틴 파울러의 말에 따르면 리팩토링의 핵심 원칙은 코드의 동작을 변경하지 않으면서 구조를 개선하는 것이다.

마치 우린 몇일에 걸쳐서 엄청난 양의 리팩토링을 진행했고, 우린 기세등등하지만 막상 업데이트 이후 유저들은 아무 반응이 없는것처럼말이다.

 

 

4. 그럼 리팩토링은 언제 해야할까?

바로 냄새(Smell)이 날때 해야한다.

마치 상한 요리처럼, 코드도 상했을수도 있다.

 

코드 스멜(Code Smell)이란?

유당불내증 있는 내가 유제품을 먹었을 때, 배가 내는 소리처럼.

코드 스멜은 '리팩토링이 필요하다는 것을 알려주는 신호' 이다.  마틴 파울러의 말이다.

 

코드 스멜도 주요 유형이 있다.

1. 중복 코드 (Duplicated Code)

2. 긴 메소드 (Long Method) / 매개변수

3. 큰 클래스 (Large Class)

4. 원시 타입 남용 (Primitive Obsession)

5. Switch문 남용 (Switch Statements)

6. 임시 필드 (Temporary Field)

 

[1 ] 중복 코드는 말 그대로 중복인 코드들이다.

public void calculateArea() {
    int area = length * width;  // 중복된 계산
}

public void calculateVolume() {
    int area = length * width;  // 동일한 로직이 다른 메소드에 반복됨
    int volume = area * height;
}

이건 너무 쉽다. 공통된 계산식을 따로 메소드로 빼서 호출하면 된다.

 

[ 2 ] 긴 메소드 / 매개변수

public void Attack(Player player, string attackInfo, Vector3 AttackPos, Vector3 AttackScale, float effectRotation, List<string> effectInfo, ...)

 

이건 내가 겪은 상황인데, 회사 일이 바쁘다보니 '일단 만들어! 그리고 부숴!' (이거 완전 토르비욘)

라는 마인드로 만들었던 공격 함수이다.

 

이건 나중에 진짜 매개변수가 많아지면 나부터도 혼란이 오게된다.

 

이 경우 난 구조체를 공격 타입별로 정의해서 관리했다.

 

예로들어 근거리/원거리/마법/영역/광역/투사체 가 있다고 가정하면

각각 구조체로 만들어 관리했다.

 

그렇다면 매개변수로 딱 하나만 던져주면 된다.

 

참고로 긴 메소드 상황은

public void processOrder() {
    validateOrder();
    calculatePrices();
    applyDiscounts();
    updateInventory();
    sendConfirmation();
}

이렇다.

 

이건 더 작은 단위로 분리해야한다. 각 메소드가 하나의 명확한 책임을 가지도록 하라는데..

어? 이거 완전 회사에서 나잖아? 대표님 저도 리팩토링 해주세요

 

[ 3 ] 큰 클래스

이것도 마치 나..

클래스에 너무 많은 역할을 부담시키면 나타나는 코드스멜인데, 

이건 실제로 신입들이 많이 저지르는 실수다. 그리고 나도?

public class OrderProcessor {
    public void validateOrder() { ... }
    public void calculatePrices() { ... }
    public void applyDiscounts() { ... }
    public void updateInventory() { ... }
    public void sendConfirmation() { ... }
    // 많은 메소드와 상태 변수들이 클래스에 존재
}

가끔 생각없이 하다보면 PlayerController에 인벤토리라던가 자잘한 코드가 들어가 있는 경우가 있다.

 

이건 단일 책임 원칙에 위배되는 행위이기에 조금 더 나누자....

 

[ 4 ] 원시 타입 남용

이건 사실 잘 와닿진 않는다.

클래스에 int string등 너무 많은 원시 타입을 남발했을때 나는 경우라는데, 

오히려 Large Class라던가 Long Method만 방지해주면 어지간해서 이게 코드스멜인가? 느껴지리는 없을거라 생각은 하는데... 아직 얼마 안된 개발자의 편협한 생각일수도 있겠다..  공부 열심히 해야지...

 

[ 5 ] Switch문 남용

Switch, if-else 이건 누구나 알고있으리라 생각한다.

 

근데 하나는 함정인게, 늘 신입 면접을 볼때, 너무 과할정도로 update함수를 안쓰거나 if-else구문을 안쓰고

이상하게 쓰잘데기 없는 UI전면자동화 만들어놓은거보면 숨이 턱 막힌다.

게다가 그게 실시간으로 작동하는거면 하...

 

[ 6 ] 임시 필드

클래스 내의 필드가 특정 상황에서만 사용될 떄 발생한다는데, 이 경우는 아직 없었다.

혹은 아직 내가 부족해서 눈치를 못챘거나??

 

크흠..

 

공부 열심히 해야겠당

'# 공부 > # 그 외 흥미로운 문제들' 카테고리의 다른 글

[백준::1966 python3] 프린터 큐  (0) 2021.12.30
[백준::5430 python3] AC  (0) 2021.12.21
[백준::11652 Py] 카드  (0) 2021.12.11
[백준::1735 Py] 분수합  (0) 2021.12.09
[백준::1475 Py] 방번호  (0) 2021.12.09