
제어문 리뷰에서 마주하는 당혹스러운 순간들
동료의 코드 리뷰를 하다가 갑자기 머리가 아파온 경험이 있나요? 분명히 문법적으로는 틀린 게 없는데, if문이 계단처럼 계속 안으로 들어가 있어서 코드의 끝이 어디인지 도저히 알 수 없는 상황 말이에요. 마치 미로 속에 갇힌 것처럼 어떤 조건이 실행되는지 파악하는 데만 한참이 걸리곤 하죠.
이런 현상을 보통 화살표 코드(Arrow Code)라고 불러요. 코드가 오른쪽으로 계속 밀려나면서 가독성이 최악이 되는 상태를 뜻하죠. 이런 코드는 단순히 보기 안 좋은 것을 넘어, 나중에 버그를 수정하거나 새로운 기능을 추가할 때 치명적인 실수를 유발하는 원인이 돼요. 특히 Go 언어는 간결함을 미덕으로 삼는 만큼, 제어문을 어떻게 사용하느냐에 따라 코드의 질이 극명하게 갈려요.
단순히 기능을 구현하는 것을 넘어, 동료들이 읽기 편하고 유지보수하기 쉬운 코드를 짜고 싶다면 Go 제어문 코드 리뷰 관점을 반드시 익혀야 해요. 지금 이 글을 읽고 나면, 복잡하게 엉킨 조건문을 어떻게 깔끔하게 펼칠 수 있는지, 그리고 어떤 패턴이 위험한 안티패턴인지 명확하게 구분할 수 있게 돼요.
이번 가이드에서는 다음과 같은 내용을 깊이 있게 다뤄볼게요.
- 코드의 가독성을 해치는 대표적인 제어문 안티패턴 분석
- 중첩된
if문을 평평하게 만드는 실전 기술 switch문을 활용해 복잡한 로직을 우아하게 정리하는 법- 실무에서 즉시 적용 가능한 Go 제어문 코드 리뷰 체크리스트
깨끗한 제어문을 작성하기 위한 사전 지식
본격적으로 코드를 고치기 전에, 우리가 왜 if와 switch를 구분해서 써야 하는지 기준을 세워야 해요. 무턱대고 switch를 쓴다고 해서 코드가 예뻐지는 건 아니거든요. 각 제어문이 가진 성격과 Go 언어 특유의 철학을 먼저 이해하는 것이 중요해요.
Go에서는 에러 처리를 위해 if err != nil 패턴을 아주 자주 사용해요. 이 과정에서 제어문이 너무 많아지면 코드의 흐름이 끊기기 쉽죠. 그래서 우리는 ‘조건의 복잡도’와 ‘경우의 수’를 기준으로 제어문을 선택하는 안목을 길러야 해요.
제어문은 프로그램의 ‘길’을 결정해요. 길을 너무 복잡하게 만들면 운전자(개발자)가 길을 잃고, 길을 너무 단순하게만 만들면 모든 상황에 대처할 수 없게 돼요. 적절한 균형이 핵심이에요.
제어문 선택을 위한 판단 기준
어떤 상황에서 어떤 제어문을 쓰는 것이 좋을지 고민된다면 아래 표를 참고해 보세요. 상황에 맞는 도구를 고르는 것만으로도 리뷰 시간을 절반으로 줄일 수 있어요.
| 구분 기준 | if문 권장 상황 | switch문 권장 상황 |
|---|---|---|
| 조건의 성격 | 범위 비교, 불리언 논리 연산 | 특정 값과의 일치 여부 확인 |
| 경우의 수 | 2~3개 정도의 단순 분기 | 4개 이상의 다중 분기 |
| 가독성 포인트 | 직관적이고 빠른 판단 필요 시 | 복잡한 매칭 로직의 나열 시 |
| 유연성 | 복합적인 논리 조건 구성 가능 | 값의 패턴이나 타입 매칭에 유리 |
단순히 if를 많이 쓴다고 나쁜 코드는 아니에요. 하지만 조건이 겹겹이 쌓여서 논리적 깊이가 깊어지는 것은 반드시 경계해야 해요. 코드 리뷰를 할 때는 ‘이 조건이 꼭 이 위치에 있어야 하는가?’를 끊임없이 스스로에게 질문해야 해요.
실전! 제어문 리팩토링 5단계 전략
이제 본격적으로 나쁜 코드를 좋은 코드로 바꾸는 기술을 배워볼게요. 단순히 이론을 아는 것과 실제 코드를 만지는 것은 완전히 다른 문제니까요. 단계별로 사례를 따라오면서 감을 익혀보세요.
STEP 1. 화살표 코드를 평평하게 펴기
가장 먼저 해결해야 할 문제는 중첩된 if문이에요. 조건이 만족되지 않으면 바로 함수를 종료해 버리는 Guard Clause(보호 구절) 패턴을 사용하면 코드를 아주 깔끔하게 만들 수 있어요. 원래 코드가 오른쪽으로 계속 밀려나고 있었다면, 이제는 왼쪽 벽에 붙어서 정렬될 거예요.
예를 들어, 사용자의 권한을 확인하고 로그를 남긴 뒤 데이터를 처리하는 로직이 있다고 해볼게요. 기존에는 권한이 있을 때만 안으로 들어가는 구조였겠지만, 리팩토링 후에는 ‘권한이 없으면 즉시 리턴’하는 방식으로 바꿉니다. 이렇게 하면 메인 로직이 함수의 가장 바깥쪽 레벨에 위치하게 되어 읽기가 훨씬 편해져요.
STEP 2. Early Return으로 인지 부하 줄이기
프로그래머의 뇌는 한 번에 처리할 수 있는 정보량이 정해져 있어요. if-else가 길어지면 ‘아까 그 조건이 뭐였지?’라며 이전 상태를 기억해야 하는 인지 부하가 발생해요. 이를 방지하려면 Early Return을 적극적으로 활용하세요.
성공 케이스를 if 안에 넣고 else로 나머지 로직을 처리하는 대신, 실패 케이스를 먼저 처리하고 함수를 끝내버리세요. 그러면 코드를 읽는 사람은 ‘아, 이 조건은 이미 해결됐구나’라고 생각하며 다음 단계로 가볍게 넘어갈 수 있어요. 이것이 바로 Go 프로그래밍의 핵심적인 스타일 중 하나예요.
STEP 3. 다중 분기는 Switch문으로 우아하게
if문이 4개 이상 늘어난다면, 그것은 switch문으로 바꿀 준비가 되었다는 신호예요. if-else if-else if... 구조는 시각적으로 매우 지저분하고, 실수로 조건을 빠뜨릴 위험도 커요. 반면 switch문은 각 케이스가 독립적으로 보이기 때문에 구조 파악이 훨씬 빨라요.
특히 Go의 switch문은 다른 언어와 달리 break를 매번 써줄 필요가 없어서 아주 간결해요. 또한, 조건식 없이 switch만 써서 복잡한 논리 구조를 깔끔하게 정리할 수도 있죠. 만약 특정 값에 따라 동작이 달라진다면, 망설이지 말고 switch를 선택하세요.
STEP 4. Type Switch를 활용한 안전한 타입 검사
Go는 정적 타입 언어지만, interface{}를 사용하다 보면 런타임에 타입이 무엇인지 확인해야 할 때가 있어요. 이때 if v, ok := i.(string); ok 같은 방식을 여러 번 쓰는 것보다, Type Switch를 사용하는 것이 훨씬 안전하고 가독성이 좋아요.
Type Switch를 사용하면 인터페이스에 담긴 실제 데이터가 무엇인지 한눈에 파악할 수 있고, 예상치 못한 타입이 들어왔을 때 `default` 문을 통해 우아하게 예외 처리를 할 수 있어요. 이는 코드의 견고함을 높이는 데 결정적인 역할을 해요.
STEP 5. 복잡한 조건식을 별도 함수로 추출하기
마지막 단계는 if문 안에 들어가는 조건식 자체가 너무 길어질 때예요. if a && b || (c && !d) 같은 코드는 보는 사람을 괴롭히죠. 이럴 때는 조건식의 의미를 담은 이름을 가진 함수로 추출하세요.
if isEligibleForDiscount(user)와 같이 작성하면, 코드는 로직을 설명하는 대신 ‘의도’를 설명하게 돼요. 리뷰어는 이제 논리 연산자를 해석하는 대신, ‘사용자가 할인 대상인지 확인하는구나’라고 바로 이해할 수 있어요. 이것이 바로 고수와 하수를 가르는 한 끗 차이예요.
리팩토링의 목적은 기능을 바꾸는 것이 아니라, 사람이 읽기 쉽게 만드는 것임을 잊지 마세요. 테스트 코드가 있다면 안심하고 구조를 변경할 수 있어요.
실제 업무에서 활용할 수 있는 리팩토링 전후 시나리오를 정리해 드릴게요.
- 시나리오: 주문 결제 시스템의 유효성 검사
- AS-IS (나쁜 예): 결제 수단 확인 -> 잔액 확인 -> 재고 확인 과정을 모두
if문 중첩으로 구현 (코드 깊이 4단계) - TO-BE (좋은 예): 각 검증 단계를 함수로 분리하고, 실패 시 즉시 에러를 반환하는 Early Return 적용 (코드 깊이 1단계)
자주 하는 실수와 해결법 및 FAQ
자주 하는 실수와 해결법
코드 리뷰 과정에서 자주 발견되는 잘못된 패턴들을 정리했어요. 이 리스트를 체크리스트로 활용해 보세요.
- ❌ 실수: 성공 로직을
if문 안에 넣고else를 남발함
왜 발생하는가: 논리 흐름을 긍정적인 방향으로만 짜려는 습관 때문이에요.
✅ 해결법: 실패 조건을 먼저 검사하고return하는 Early Return을 사용하세요. - ❌ 실수:
switch문에서default케이스를 누락함
왜 발생하는가: 모든 경우의 수를 다 알고 있다고 자만하기 때문이에요.
✅ 해결법: 예상치 못한 값이 들어왔을 때를 대비해 반드시default를 작성하여 에러를 처리하세요. - ❌ 실수: 하나의
if문에 너무 많은 논리 연산자(&&, ||)를 사용함
왜 발생하는가: 코드를 짧게 만들려는 욕심 때문이에요.
✅ 해결법: 복잡한 조건은 별도의 변수에 담거나, 의미 있는 이름의 함수로 분리하세요. - ❌ 실수:
if err != nil이후에return을 빼먹음
왜 발생하는가: 에러 처리를 단순한 알림으로 생각하기 때문이에요.
✅ 해결법: 에러가 발생했다면 더 이상 로직을 진행하지 않도록 즉시 함수를 종료하세요. - ❌ 실수: 단순한 값 비교인데
switch문 대신 긴if-else를 사용함
왜 발생하는가:switch문의 문법이 익숙하지 않아서일 수 있어요.
✅ 해결법: 비교 대상이 명확한 다중 값 매칭이라면switch를 사용하여 가독성을 높이세요.
자주 묻는 질문
Q. Go에서는 왜 if err != nil 패턴을 그렇게 강조하나요?
Go의 설계 철학은 에러를 예외(Exception)가 아닌 일반적인 값으로 다루는 거예요. 따라서 제어문에서 에러를 즉시 확인하고 처리하는 것이 프로그램의 흐름을 명확하게 유지하는 가장 핵심적인 방법이기 때문이에요.
Q. switch문이 if문보다 성능상 더 빠른가요?
대부분의 경우 성능 차이는 미미해요. 성능 최적화보다는 가독성과 코드의 구조적 명확성을 기준으로 선택하는 것이 훨씬 현명한 전략이에요. 코드는 컴퓨터가 아니라 사람이 읽는 것이니까요.
Q. 중첩된 if문을 무조건 다 펴야 하나요?
아니요, 무조건은 아니에요. 정말로 논리적인 인과관계가 깊게 얽혀 있고, 코드를 펴는 것이 오히려 더 복잡해 보인다면 적절한 수준의 중첩은 허용될 수 있어요. 하지만 ‘읽기 힘들다’는 느낌이 들면 즉시 리팩토링을 고려해야 해요.
Q. default 문을 쓸 때 주의할 점이 있나요?
default 문에는 단순히 아무것도 안 하는 코드를 넣기보다, 예상치 못한 상황에 대한 로그 기록이나 에러 반환을 넣는 것이 좋아요. 그래야 나중에 버그가 발생했을 때 원인을 찾기가 쉬워져요.
성공적인 코드 리뷰를 위한 마지막 단계
지금까지 Go 제어문 코드 리뷰 관점에서 어떻게 하면 더 나은 코드를 작성할 수 있는지 살펴보았어요. 제어문은 단순한 문법을 넘어, 프로그램의 논리적 흐름을 결정하는 설계의 핵심이에요. 오늘 배운 내용을 잊지 않도록 아래 체크리스트를 꼭 확인해 보세요.
- 코드의 깊이가 깊어지면(화살표 코드) 즉시 Early Return을 고려하세요.
- 2~3개의 단순 조건은
if, 4개 이상의 다중 값 매칭은switch가 유리해요. if문 내부의 복잡한 논리식은 함수로 추출하여 의도를 드러내세요.switch문을 쓸 때는 반드시default케이스를 통해 예외 상황을 대비하세요.- 타입 검사가 필요할 때는
Type Switch를 사용하여 안전하게 처리하세요. - 에러 처리 시에는 반드시
return을 사용하여 흐름을 제어하세요.
이제 여러분이 할 일은 명확해요. 오늘 작성한 코드 중 가장 if문이 많은 부분을 찾아보세요. 그리고 방금 배운 Guard Clause를 적용해 코드를 옆으로 밀지 않고 아래로 흐르게 만들어보는 거예요. 작은 변화가 모여 거대한 코드베이스의 품질을 결정한답니다.
더 깊이 있는 Go 프로그래밍 실력을 쌓고 싶다면, 제어문뿐만 아니라 전체적인 언어 구조를 이해하는 과정이 필요해요. Go 제어문 완전 정복 가이드 글과 함께 읽으면 학습 효과를 배로 높일 수 있어요. 관련 시리즈 글도 함께 읽고 실력을 완성하세요.