거의 5년만에 코딩을 해봤다. 물론 서비스 시스템을 위한 코드 작성이 아니다. 이 시간 동안 코드에 손대지 않았다면 개발자라고 이야기하는 것도 무색하다. 사실 보기에 갑갑하다고 나서서 코드 작업했다가 문제를 일으키기도 했다. 라이브는 살아있는 것이기 때문에 현직 개발자가 하는게 맞지. 특히나 IntelliJ를 써서 코드를 작성해본 건 꽤 오래된 것 같다. 마지막 코드 작업은 VS Code 가지고 React로 FE App을 개발했었으니 말이다.
라이엇게임즈 마지막 개발 FE App
대학교 특강을 땜빵해달라는 요청이 있었고, 주제가 TDD(Test Driven Development)였다. 현대오토에버 합류 이후 코드 짤 시간도 생각도 없었다. 다른 이유로 오토에버의 개발 환경 개선 작업을 주도하고 있기 때문에 환경이 구축된 이후에 뭘 하더라도 할려고 했다. 덕분에 맥에 아껴두고 깔지 않았던 IntelliJ도 설치했다. 그리고 감동했다. 감동의 이유에 대해서는 조만간 다른 글을 통해 이야기하겠지만… 아는 사람들은 감동할 것이다.
특강을 대신 부탁하신 분이 IDE(IntelliJ)의 Refactoring 기능 활용 방법을 꼭! 잘 전달해달라는 요청이 있었다. 특히 강의를 마우스로 메뉴따라 하면 학생들이 혼돈의 도가니기가 되기 때문에 꼭 단축키를 이용하라는 조언도 해주셨다. 사실 이게 말이 되는게 강의장에서 학생들에게 “어떤 메뉴 > 어디 세부 메뉴 > …” 이렇게 언급을 하면 왜 다시 질문하는 학생들이 그렇게 많은지. 그리고 엉뚱한 질문에 답하고 나면 정말 시간이 빨리 간다. 역시 정답은 단축키! 도움말에 나오는 단축키 목록을 가만히 보고 있자니 대강 생각나는 것 같다. 몸이 기억한다는게 이런 느낌일까?
TDD를 적용할 예시로 계산기를 염두에 두고 있어서, 메이븐 프로젝트부터 셋업해서 차근차근 마음대로 코드를 작성했다.껍데기 코드도 대강 만들고, 깨지는 테스트 케이스도 만들었다. IDE 화면을 왔다갔다하면서 코드를 작성하자니, 예전이라고 하기에도 더 먼 옛날에 코드짜던 추억도 떠오르고… 감회라는게 이럴 때 쓰는 단어라는 생각이 났다.
TDD 특강 이야기를 듣자마자 생각이 들었던 건 이제 단축키가 아니라 AI 코딩이었다. 이제는 단축키로 코드를 효과적으로 짜는게 실력이 아니라 AI Assist 기능을 제대로 활용하는 것이 엔지니어의 능력이라고 생각했고, 학생들에게도 변화된 세상에 맞춰 준비를 해야한다는 이야기를 하고 싶었다. 그리고 쏘카때부터 코파일럿을 사람들에게 쓰라고 했고 50% 이상 생산성 향상이라는 답을 들었다. 그리고 최소 50% 향상이라는 이야기를 깃헙 CTO로부터 듣기도 했다. 듣기만 했지 코드로 직접 해보질 않았기 때문에 이번 기회에 스스로도 써보고 정말 그 정도인지를 확인하고 싶었다.
먼저 테스트 코드를 Parameterized Test를 활용해 Refactoring 해달라고 했다.
Before
@Test void shouldRunToCalculate() throws UnsupportedCalculationException { assertThat(calculator.run(1, Calculator.OPERATOR.PLUS, 1)).isEqualTo(2); } @Test void shouldCalculateMorePlusCases() throws UnsupportedCalculationException { assertThat(calculator.run(1, Calculator.OPERATOR.PLUS, 2)).isEqualTo(3); }
After
@ParameterizedTest @CsvSource({ "1, PLUS, 1, 2", "1, PLUS, 2, 3" }) void shouldCalculateCorrectly(int left, Calculator.OPERATOR operator, int right, int expected) throws UnsupportedCalculationException { assertThat(calculator.run(left, operator, right)).isEqualTo(expected); }
구체적인 지침을 줬기 때문에 기대했던 만큼 결과가 나온다. 우선 테스트 선수라는 이야기는 맞는 것 같다. 빠른 작업을 위해 단축키를 쓸 필요가 없다. 그냥 영역을 선택해서 코파일럿으로 적절한 Directive를 주는 것만으로 결과를 얻었다. 동일한 코드를 4o-mini를 통해 실행했을 때 JUnit4 기준으로 결과를 생성했지만, JUnit5로 테스트 결과를 생성하라고 추가 Directive를 줬더니 유사한 코드를 생성했다.
테스트 코드에 대해서는 기대만큼의 결과를 얻을 수 있다는 것을 확인했으니, 메인 코드 Refactoring을 진행했다. 간단 코드로 사전에 작성한 코드는 아래와 같다.
public int run(int leftValue, OPERATOR operator, int rightValue) throws UnsupportedCalculationException { if (operator == OPERATOR.MINUS) { return leftValue - rightValue; } else if (operator == OPERATOR.PLUS) { return leftValue + rightValue; } else { throw new UnsupportedCalculationException(); } }
위 코드에서 Refactoring의 방향에 대해 별다른 지침을 주지 않았지만, “가독성, 확장성, 중복 제거” 관점의 결과라는 이야기와 함께 결과를 만들었다. 그리고 이 결과를 다음의 방안에 착안해서 진행했다고 코멘트를 남겼다.
- if-else → switch 구문으로 가독성 개선
- 향후 연산자가 추가될 수 있도록 enum 내에 BiFunction을 활용한 전략 패턴 도입
- UnsupportedCalculationException을 연산자 자체가 책임지도록 설계
public int run(int leftValue, OPERATOR operator, int rightValue) throws UnsupportedCalculationException { if (operator == null) { throw new UnsupportedCalculationException("Operator cannot be null"); } return operator.apply(leftValue, rightValue); } public enum OPERATOR { PLUS((a, b) -> a + b), MINUS((a, b) -> a - b); // 향후 MULTIPLY 추가 시: MULTIPLY((a, b) -> a * b) private final BiFunction <Integer, Integer, Integer> operation; OPERATOR(BiFunctioninteger<Integer, Integer, Integer> operation) { this.operation = operation; } public int apply(int a, int b) throws UnsupportedCalculationException { if (operation == null) { throw new UnsupportedCalculationException("Unsupported operation for operator: " + this.name()); } return operation.apply(a, b); } }
내가 선호하는 스타일 방식으로 거의 완벽하게 Refactoring 결과를 만들었다. 앞서 리팩터링한 테스트를 통해 결과가 수행되는지 확인했다. 완벽하게 동작한다. 여기에 더해 곱하기 부분은 나중에 구현할 것이고, 이를 메인에서 Exception 처리하는 것이 만족스럽지 않기 때문에 이 부분만 코드로 전환할 필요가 있었다. 다음의 Prompt를 추가해줬다.
BiFunction대신 Operation을 써서 추상화해줘.
MULTIPLY를 추가해주고 Not implemented 라는 메시지로 Exception을 발생시키도록 해줘.
그리고 최종적으로 다음의 코드로 Refactoring을 마무리했다.
public enum OPERATOR { PLUS((leftValue, rightValue) -> leftValue + rightValue), MINUS((leftValue, rightValue) -> leftValue - rightValue), MULTIPLY((leftValue, rightValue) -> { throw new UnsupportedCalculationException("Not implemented"); }); private final Operation operation; OPERATOR(Operation operation) { this.operation = operation; } public int apply(int leftValue, int rightValue) throws UnsupportedCalculationException { return operation.apply(leftValue, rightValue); } } @FunctionalInterface public interface Operation { int apply(int leftValue, int rightValue) throws UnsupportedCalculationException; }
여기까지 과정에서 IntelliJ에서 사용한 단축키는 Cmd + R 혹은 실행 범위를 변경하기 위해 Shift + Cmd + R 단축키만 사용했다. 단축키 빌런은 앞으로 세상에 필요없을 것 같다.
느낌 정리
정말 세상은 너무 빠르게 변하고 있는 것 같다. 특히 개발 분야에서 더욱 그렇다. 쏘카에서 코파일럿을 도입시킨 3년 전에 다른 리더들과 당시의 개발자가 얼마쯤이면 필요없어질 것인가를 가지고 이야기를 나눈 적이 있었다. 나의 예측은 앞으로 5년 후면 신입 개발자에게 요구하는 코딩 수준은 AI가 어느 정도 대체할 것이었다. 이번 결과를 돌려보면서 느낀 점은 나의 예상이 틀렸다는 것이다. 시니어와 페어를 이룬 신입 개발자를 가정한다면 이미 AI는 일반적인 Java 코드 수준에서는 신입 개발자를 앞서고 있다. 적절한 Directive를 줄 수 있는 시니어 개발자라면 코파일럿 혹은 Gen AI 기반의 Assist 기능을 활용해 4~5년차 수준의 개발자를 한 명과 함께 일하는 것과 비슷한 Performance를 얻을 수 있다.
내 경험이 틀린 것인지 한번 더 검증하기 위해 간단한 RESTful endpoint를 가지는 Springboot 기반 어플리케이션을 만들어봤다. 예상 목표 시간은 30분이었는데, 거의 30분만에 Working Code를 완성할 수 있었다. 테스트 코드까지를 포함해 실제 전체 형상을 완성하는데는 1시간 이내가 걸렸다. 아마도 AI Assist의 도움을 받지 않고, 예전 방식대로 자료 찾고, Javadoc 뒤지고, Stackoverflow에서 edge 케이스를 함께 확인하는 시간을 썼더라면 최소 3시간에서 하루 정도는 썼을 것 같은데, 한 시간안에 Local 환경에서 동작하는 WebApp을 만들 수 있다는게 놀랍기만 했다.
시대는 진화하고 있다. 개발자의 역할 역시 바뀌는 시대에 맞춰 재정의가 이뤄져야 한다. AI와 경쟁하는 것이 아닌 AI를 도구로써 효과적으로 다루며 개발이라는 업에서 자신의 가능성을 입증할 수 있는 사람이 되어야 한다.