[WEB발신]너는이코드를존중해야한다

프로필

2025년 12월 31일

154 0

요즘 포스팅이 뜸했던 이유

요즘 포스팅이 뜸했다.
뜸했다기보단 지금 보니까 마지막 포스팅으로부터 거의 반년이 지났다.
그동안 뭘 하느라 블로그를 유기했냐고 물어본다면 딱히 거창한 이유는 아니다.

  • 도메인 만료가 다가와서 새 도메인으로 이전했고
  • 끝나지 않을 블로그 리팩토링을 진행중이고, 사실 리팩토링이라기보단 처음부터 다시 만드는 중이다
  • 사업하는 친구의 웹 어플리케이션을 제작했고, 내 편의를 위해서 이것저것 자동화 어플리케이션을 제작했다
  • 마지막으로 가장 큰 약점이라고 생각하는 코딩 테스트와 CS 공부를 진행중이다.

정리하자면 눈에 보이는 결과물보다는 사고 과정 쪽에 시간을 쓰고 있다.


그래서 뭔 코드를 존중해야 하냐고?

이 코드다.

def solution(num_list):
    return [even := sum(i % 2 == 0 for i in num_list), len(num_list) - even]

이 코드는 num_list 안에 들어있는 짝수와 홀수의 개수를 배열로 반환한다.

프로그래머스 0레벨 '홀수 짝수 개수' 문제다.
조건은 다음과 같다.

  • 리스트 길이 : 1 ≤ n ≤ 100
  • 원소 범위 : 0 ≤ 값 ≤ 1000

딱히 어려운 문제는 아니다. 그리고 가장 정석적인 풀이로는 이렇게 풀 수 있다.

even, odd = 0, 0
for i in num_list:
    if i % 2 == 0:
        even += 1
    else:
        odd += 1
return [even, odd]

이 코드가 나쁘다는 말은 아니다.
다만 코딩 테스트라는 맥락에 한해서는 내 선택지가 아니다.

미리 실드를 하나 깔아놓자면 나는 실무에서 사용하는 코드와 코딩 테스트에서 사용하는 코드를 철저하게 구분한다.
실무에서는 가독성과 협업, 유지보수 등을 고려하는 편이고, 코테에서는 할 수 있는 한 최대로 온몸을 비틀어보는 성향이다.

즉, 이 글은 실무 코드를 자랑하려는 글이 아니다. 사고 과정을 보여주려고 작성하는 글이다.


사고 과정 #1

sum(1 for i in num_list if i % 2 == 0)

짝수면 1을 반환하고 sum으로 더한다. 이걸로 짝수 개수를 구할 수 있다.

그렇다면 홀수도?

[sum(1 for i in num_list if i % 2 == 0), sum(1 for i in num_list if i % 2 == 1)]

이미 정답이다. 근데 이 코드는 num_list를 두 번 순회하고, 같은 조건을 두 번 계산한다.
그렇지만 이 문제에서 배열의 길이는 최대 100이므로 성능상에 큰 하자는 없다.

미적 감각만큼 개개인의 의견이 크게 갈리는 문제도 드물겠지만 일단 내 기준에서 이 코드는 아름답지 못하다.


사고 과정 #2 : 왈러스 연산자

왈러스 연산자가 뭐냐고?
Python 3.8에서 추가된 문법이다.

Walrus Operator (:=) 표현식 내부에서 값을 계산하면서 동시에 할당

이 문제에서 이 연산자가 딱 맞는 이유는 하나다.
짝수 혹은 홀수 중 하나의 개수만 구하면 나머지는 굳이 계산할 필요가 없기 때문이다.

하지만 물론 그 방법이 왈러스만 있는 것은 아니다.

even = sum(1 for i in num_list if i % 2 == 0)
return [even, len(num_list) - even]

이렇게 풀어도 중복 계산은 제거할 수 있고, 이 정도면 됐다고 느낄 수 있다.

하지만 여기서 왈러스가 등장한다면?

[even := sum(1 for i in num_list if i % 2 == 0), len(num_list) - even]

가독성과 협업성, 유지보수성은 개나 줘버리지만 나 같은 숏코딩 힙스터 정신병자가 딱 좋아할 만한 코드가 나온다.


사고 과정 #3

여전히 개선의 여지가 남았다.
파이썬에서 True는 1, False는 0으로 계산된다.
즉, 1 for i로 조건 분기를 하지 말고 boolean 연산으로 진행하는게 오히려 C 레벨에서 연산이 되기 때문에 더 효율적이라는 생각이 들었다.

sum(i % 2 == 0 for i in num_list)

물론 이 차이가 체감이 되는 정도는 아니고, 배열의 최대 길이가 100인 이 문제에서는 사실상 큰 의미는 없다.
다만 조건 분기를 줄이고, 파이썬의 타입 특성을 그대로 사용하는 방식이라는 점에서 개선을 진행했고, 이런 사고 과정들을 거쳐 최종 코드가 완성되었다.

def solution(num_list):
    return [even := sum(i % 2 == 0 for i in num_list), len(num_list) - even]

0레벨 문제를 이렇게 푸는 사람은 많지 않을 거라고 생각한다.
그렇다고 해서 이 코드가 장난으로 쓴 코드는 아니다.


실제로 효율적인 코드인가?

미리 언급한 가독성, 협업성, 유지보수성을 제외한 시간 복잡도, 공간 복잡도 측면에서 과연 이 코드는 효율적일까?

시간 복잡도
- 리스트 단일 순회 -> O(n)
- 나머지 연산 -> O(1)

문제 조건상 이보다 나은 시간 복잡도는 존재하지 않는다. 왜냐하면 배열의 모든 원소를 최소 한 번은 무조건 순회해야하기 때문이다.

공간 복잡도
- 제너레이터 사용
- 불필요한 추가 배열 생성 없음
- 유지되는 변수는 even 하나

공간 복잡도는 O(1)이 나온다.

하지만 실무에서는?

def solution(num_list):
    even = sum(i % 2 == 0 for i in num_list)
    return [even, len(num_list) - even]

혹은 그냥 for문을 쓴다. 협업 코드에서의 왈러스는 오히려 컨벤션상 권장되는 극소수의 경우를 제외하면 방해가 되기 때문이다.


결론

이 코드를 쓰라는 게 아니다. 다만 간단한 편에 속하는 문제라도 다양한 방법으로 풀어보면서 이런 문법이 있다는 걸 배우고, 응용하며 적재적소에 쓸지 말지를 의식적으로 선택할 수 있다는 것 자체가 중요하다고 생각한다.

몰라서 안 쓰는 것과 알고 안 쓰는 것은 전혀 다른 이야기이기 때문이다.

이 포스팅을 작성하게 된 건, 지금 내가 가진 코드의 미학이 1년 뒤, 혹은 5년 뒤에도 같을 거라고 장담할 수 없다. 또 새로운 기술을 배우고, 사고방식이 바뀌게 되면 오히려 지금의 코드를 욕할 수도 있다. 허나, 그 길에 다다른 과정 또한 중요하다고 생각하기에 2025년의 나는 이런 철학을 가졌으며 이런 사고를 하고 있었다는 걸 기록으로 남기고 싶어서이다.

사실 % 2 == 0의 비교 연산조차 사치라고 생각하는 병자들을 위한 비트 연산을 이용한 방법도 있는데

return [len(num_list) - (odd := sum(i & 1 for i in num_list)), odd]

내 기준에서 얘는 내 자신이 쓴 코드여도 뺨이 마려울 것 같기도 하고, 결정적으로 인간에게 직관적인 로직조차 아니고, 또한 파이썬 인터프리터 오버헤드를 고려했을 때 내 코드와 큰 차이는 없어서 이쯤에서 마무리를 짓기로 했다.

마지막으로 혹시 이 문제를 람다를 제외한 다른 방식으로 푼 사람이 있다면 댓글로 남겨주길 바란다.
문제보다 생각이 더 재미있다면 그 코드에서는 배울 점이 있다고 생각하기 때문이다.

#숏코딩 #힙스터 #왈러스 #코딩테스트

댓글 0개

댓글을 작성하려면 로그인이 필요합니다