읽지 않는 코드의 시대
Source: NHN Cloud Tech Blog
본 콘텐츠는 작성자가 사내 게시판에 공유한 글을 가공한 것으로, 작성자의 의도와 맥락을 충실히 전달하기 위해 원문의 문장 스타일을 그대로 유지하였습니다.
서론
나는 오랫동안 아름다운 코드를 꿈꿔 왔다.
순수 함수들이 물 흐르듯 합성되고, 타입 시스템이 버그를 원천적으로 불가능하게 만들며, 수학적 증명처럼 견고한 로직이 펼쳐지는 그런 코드. 우리는 그것을 **‘장인 정신’**이라 불렀고, 그 경지에 도달하기 위해 범주론을 공부하고, 타입 레벨 프로그래밍을 이해하려 씨름했다.
그러나 어느 날 문득 깨달았다. 더 이상 아무도 코드를 읽지 않는다는 것을.
가독성의 정의가 바뀌다
‘가독성’이라는 단어의 의미가 조용히, 그러나 근본적으로 변하고 있다.
과거의 가독성은 인간의 인지를 위한 것이었다.
- 개발자가 로직을 머릿속에서 추적할 수 있는가
- 동료가 코드 리뷰에서 오류를 발견할 수 있는가
- 6개월 후의 내가 이 코드를 이해할 수 있는가
우리는 이 질문들에 답하기 위해 클린 코드(Clean Code) 를 논했고, SOLID 원칙을 세웠으며, 디자인 패턴이라는 공통 언어를 만들었다. 모두 인간의 제한된 작업 기억 용량 안에서 복잡성을 다루기 위한 몸부림이었다.
하지만 이제 가독성은 기계의 패턴 인식을 위한 것이 되어 가고 있다.
- AI가 이 코드의 패턴을 학습 데이터에서 본 적 있는가
- AI가 수정 요청을 받았을 때 정확한 위치를 찾을 수 있는가
- AI가 로컬 변경을 가했을 때 전체 시스템이 깨지지 않는가
두 가지 가독성은 때로 겹치지만, 본질적으로 다른 것을 최적화한다.
- 인간을 위한 가독성은 추상화와 압축을 추구한다.
- 기계를 위한 가독성은 명시성과 예측 가능성을 추구한다.
장인 정신의 비극적 위치
함수형 프로그래밍이 약속했던 것은 명확했다. 인간의 인지적 한계를 코드로 극복하는 것.
- 참조 투명성: 코드의 어떤 부분이든 독립적으로 추론 가능
- 불변성: 시간에 따른 상태 변화를 머릿속에서 추적하는 부담 감소
- 강력한 타입 시스템: 컴파일러가 오류를 잡아줌
이 모든 것이 인간의 한계를 보완하기 위한 도구였다.
그러나 AI에게는 보완할 한계가 없다. AI는 수백만 개의 코드베이스를 학습했고, 패턴 매칭의 원시적 힘으로 무장했다. 인간의 인지 부하를 줄여주는 우아한 추상화는 AI에게는 노이즈에 가깝다. AI는 평범하고 반복적인 명령형 코드를 더 익숙하게 받아들인다.
“배우기는 어렵지만, 장기적 이점이 있습니다.” – 함수형 프로그래밍 지지자
AI가 명령형 프로그래밍의 진입 장벽을 무너뜨렸다. 이제 누구나 AI의 도움으로 명령형 코드를 빠르게 작성할 수 있다. 반면 함수형 프로그래밍의 학습 곡선은 여전히 가파르다. 더 쓸쓸한 것은 함수형 프로그래밍의 아름다움을 감상할 줄 아는 사람들마저 코드를 직접 읽지 않는다는 사실이다. 그들의 Claude Code가 대신 읽는다.
Claude >> @PureFunctional OrderService::doStuff의 로직을 분석해서 설명해 줘
매개체로 전락한 코드
코드의 존재론적 지위가 바뀌고 있다.
과거에 코드는 인간의 영역이었다. 우리는 그 안에서 살았다. 매일 읽고, 고치고, 확장했다. 변수명 하나에 고민하고, 함수의 위치를 두고 토론했다. 코드는 우리의 생각이 물질화된 것이었고, 그래서 우리는 그것의 아름다움에 신경 썼다.
이제 코드는 인간의 의도와 기계의 실행 사이를 잇는 매개체가 되고 있다.
인간의 의도 → 자연어 명세 → AI → 코드(누가 신경이나 쓰는가) → 실행
이 모델에서 함수형 vs 객체지향 논쟁은 x86 vs ARM 논쟁과 비슷해진다. 특정 상황에서 성능 차이가 있을 수 있지만, 더 이상 인간이 거주하는 층위가 아니다. 우리는 목수에서 건축주가 되어 가고 있다.
새로운 미학의 등장
AI 시대의 ‘좋은 코드’란 무엇인가? 새로운 미학이 필요하다.
관용적 표현이 최적화를 이긴다
// AI 친화적: 수백만 번 본 패턴
users.stream().filter(User::isActive).toList();
// AI 비친화적: 이게 뭐 하는 코드지?
users.stream().reduce(new ArrayList<>(),
(acc, u) -> { if(u.isActive()) acc.add(u); return acc; },
(a, b) -> { a.addAll(b); return a; });
AI는 익숙한 것을 잘 다룬다. 창의적인 최적화보다 평범한 관용구가 더 안전하다.
추론의 지역성
함수를 이해하는 데 필요한 모든 것이 눈에 보이거나, 한 번의 점프로 도달 가능해야 한다. Action at a distance는 금물이다. Dependency injection 트릭, aspect weaving, runtime proxy 등 암묵적 메커니즘은 AI의 추론을 방해한다.
예측 가능한 구조
/order
OrderController.java
OrderService.java
OrderRepository.java
Order.java
AI는 관습에서 맥락을 추론한다. 파일 위치를 예측할 수 있으면 탐색 비용이 줄어든다. 관습은 압축된 정보다.
명시적 상태 전이
// AI 친화적: 가능한 상태가 명시적으로 열거됨
enum Status { DRAFT, SUBMITTED, FULFILLED }
// AI 비친화적: 플래그 조합으로 상태를 유추해야 함
boolean isSubmitted;
boolean isFulfilled;
boolean isDraft;
열거형으로 상태를 선언하면 AI는 전체 그림을 한눈에 파악한다. 여러 boolean 플래그를 조합해야 하는 코드는 AI도 길을 잃는다.
장황하지만 명확한 이름
// AI 친화적: 이름만 봐도 의도가 명확
public boolean isEligibleForRefundBasedOnPurchaseDateAndMembershipStatus()
// AI 비친화적: 주석에 의존
/** 구매일과 멤버십 상태에 따라 환불 가능 여부를 판단한다 */
public boolean canRefund()
인간에게는 간결한 이름과 상세한 주석이 읽기 좋을 수 있다. 그러나 AI는 주석보다 코드 자체를 신뢰한다. 함수명 자체가 의도를 담고 있으면 별도의 맥락 없이도 정확히 활용할 수 있다.
작은 파일, 단일 책임
100줄짜리 파일 10개가 1000줄짜리 파일보다 낫다. AI의 컨텍스트 윈도우는 실질적 제약이다. 작은 파일은 전체를 교체하거나 부분을 수정하기 쉽다.
명세로서의 테스트
@Test
void shouldRejectOrderWhenInventoryInsufficient() { /* ... */ }
AI는 테스트를 읽고 코드의 의도를 역으로 파악한다. 테스트 이름이 곧 요구 사항이 되고, 테스트 본문이 곧 예제가 된다.
아이러니: 함수형의 귀환
흥미로운 반전이 있다. AI 친화적 코드의 원칙들—불변성, 명시적 상태, 작고 순수한 함수—은 함수형 프로그래밍의 원칙과 상당히 겹친다. 함수형 프로그래밍은 살아남을 것이다. 그것이 아름다워서가 아니라, 기계가 읽기 좋아서.
고차원적 추상화와 이론적 우아함은 사라질지라도, 불변 데이터, 순수 함수, 명시적 타입은 남는다. 미학은 바뀌었지만 핵심 원칙들은 다른 이유로 생명을 얻었다.
결론
우리는 아름다운 시대의 끝자락에 서 있다.
- 코드를 매개로 인간과 인간이 소통하던 시대
- 변수명 하나에 의도를 담고, 함수 구조로 사고 흐름을 표현하던 시대
그 시대가 저물고 있다. 코드는 점점 인간의 눈을 거치지 않는 영역으로 이동한다. 우리가 코드 안에서 살았기에 그 아름다움에 신경 썼듯, 코드 밖으로 나가면 그 아름다움은 의미를 잃는다. 장인 정신은 LP 레코드나 기계식 시계처럼—과정을 사랑하는 이들의 영역으로 남게 될 것이다. 시장은 우아함을 알아보지 않는다. 시장은 속도를 원한다. AI가 명령형 코드의 속도를 거의 무한히 빠르게 만들어 버렸다.
그럼에도 우리는 가끔, 퇴근 후 조용한 사무실에서 완성한 순수 함수 체인이 테스트를 통과하며 초록불이 켜지는 순간을 기억한다. 타입 시스템이 버그를 컴파일 타임에 잡아줄 때의 희열을. 코드가 단지 작동하는 것을 넘어 증명이 될 때의 만족을.
아름다운 추억이다.

