
오늘은 써야지 라는 개발자 블로그 글쓰기 모임에 참여하게 되었다.
이 모임은 매주 개발 관련 블로그를 작성하고, 서로의 글을 공유하며 피드백을 나누고 경험과 인사이트를 나누자는 취지로 개설된 자리이다.
첫 주차의 주제는 '이제와서 고쳐보는 2024년의 내 코드'이다.
이번 포스팅은 2024년에 겪은 일들과 기존 코드의 문제점들과 한계점, 이를 어떻게 해결해나가야 할지, 앞으로 어떤 방향으로 나아갈 것인지에 대해 써보려 한다.
2024년은 큰 변화의 해
2024년은 내 개발 인생에 있어서 아래와 같은 큰 변화를 겪어, 큰 전환점이 있었던 해였다.
- TDD와 클린 아키텍처 도입
항해플러스를 통해 TDD를 더욱 깊게 공부하게 되었고, 클린 아키텍처 원칙을 적용해서 관심사를 분리하고 의존도가 낮은 시스템을 설계하는 방법을 배웠다. - 이직을 통한 기술 스택의 확장
실무 환경이 Java에서 Kotlin으로 전환하면서 문법의 간결함과 생산성을 체감하였다. 또한 NoSQL을 사용하기 시작하여 모델링의 유연성을 경험했고, Reactive 기반의 시스템을 Webflux와 Coroutine을 사용해 비동기 처리를 구현할 수 있게 되었다.
내 기존 코드들의 문제점과 한계점
새로운 기술과 방법론을 접하면서 지난 프로젝트들을 돌아보게 되었는데, 다음과 같은 한계점들이 눈에 띄게 드러났다.
- 구조적 복잡성
퇴근 후 짧은 시간씩 개발하다 보니 중복 코드와 모듈 간의 경계가 모호해진게 눈에 보였다. 작은 변경에도 사이드이펙트가 발생하는 상황들이 매우 빈번했다. - 테스트의 부재
TDD의 중요성을 깨닫기 전 작성된 코드들이라 기능 개선 시 디버깅과 리팩토링 과정에서 어려움이 있을 것이라 판단된다. - 3-Layered-Architecture의 한계
기존 코드는 presentation layer, business layer, data-access layer 3계층 구조로 구성되어 있지만, 이 아키텍처의 한계로 인해 유스케이스 로직과 서비스 로직이 명확하게 분리되지 않았다. - DIP 미적용에 따른 의존성 문제
기존 코드는 의존관계 역전 원칙(DIP)를 적용하지 않아, 외무 모듈과의 의존성이 지나치게 높았다. 이로 인해 외부 모듈의 변경이 실제 비즈니스 로직에 영향을 미치는 위험이 매우 컸다. - 기존 기술 스택의 한계
이직 전에는 Java, RDBMS, blocking 기반 시스템만 사용해서 개발해왔다. 이러한 기술 스택은 동시성 처리, 응답성 향상, 확장성 등 최신 요구사항에 부합하는 비동기 처리와 유연한 데이터 모델링 구현에 한계가 있음을 절실히 느끼게 했다.
실제로 기존 프로젝트 중 챗봇을 개발하던 것이 있었는데, Non-Blocking으로 구현을 해야 하던 것을 ReactiveMongo 또는 ReactiveRedis를 사용하는 법을 모른다는 이유로 급하게 동기 방식으로 DB 접근을 했던 기억이 있다.
그래서 어떻게 개선해야 할까?
문제점들을 인식한 다음, 다음과 같은 개선 방향을 설정하고 실행하였다.
1. 중복 제거와 모듈화
반복되는 로직들을 하나의 모듈로 통합해 재사용성을 높이고자 하였다. 여러 모듈이나 클래스에서 동일한 데이터 검증, 포맷 변환, 로깅 등의 작업이 반복적으로 작성된 경우를 발견하여 이러한 공통 기능을 별도의 유틸리티 클래스나 헬퍼 함수로 분리해, 각 모듈에서 해당 기능을 호출하도록 수정했다.
예를 들어, 문자열 포맷 변환이나 날짜 계산 로직이 여러 곳에 중복되어 있다면, DateUtils나 StringUtils 같은 공통 모듈을 만들어 한 번만 구현하고 재사용하도록 했다.
또한, 시스템 전반에 흩어져 있던 기능들이 단일 파일이나 모듈에 집중되어 있었기 때문에, 특정 기능의 변경이 다른 부분에 예기치 않은 영향을 미치고 있었는데, 이는 기능별로 책임을 명확히 나누어, 각 모듈이 독립적으로 동작하도록 리팩토링 작업을 하였다.
2. 명확한 계층 분리 및 DIP 적용
의존관계 역전을 이용하여 외부 모듈과의 결합도를 낮추어 각 모듈 간 경계를 명확하게 했다.
이 부분에 대해서 조금 더 자세히 설명을 하자면, 아래와 같은 방식으로 인터페이스의 특징을 이용해, 의존 관계를 거꾸로 하여 도메인 계층을 가장 안쪽으로 두어 보호할 수 있다는 것이다. 예를 들어 User의 RefreshToken을 DB에 넣고 있었는데, Redis에 넣도록 변경하고 싶다면 아래 그림과 같이 도메인 계층의 UserRepository 인터페이스는 수정할 필요 없이 구현체 부분에만 RedisTemplate를 사용하는 방식으로 변경해주면 되는 것이다.

3. Application 계층 도입
유연한 어플리케이션을 설계하기 위해선 객체 간의 협력 관계를 고려해야 한다. 이를 통해 각 객체들의 책임을 명확히 하고, 각 책임이 어떤 행동을 하게 할 것인지 고민해야 한다. 즉 도메인에 집중해야 한다.
그리하여 Application 계층을 도입하고 아래처럼 4-layered-Architecture로 전환을 하게 되었다.

4. 테스트 자동화 강화 (TDD, Kotest, MockK 도입)
기존에 Junit과 Mock/Stubbing 기법 대신, Kotlin 환경에서 더 자연스럽게 사용할 수 있는 Kotest와 MockK를 활용하여 테스트 코드를 작성했다. 아직은 조금 어색한 감이 있지만 기능 구현 전 테스트코드를 먼저 작성하고, 변경 사항 발생 시에도 안정적인 리팩토링을 수행할 수 있도록 하였다. 이렇게 작업을 하면서 단위 테스트와 통합테스트를 체계적으로 구현하였고, 기능 구현 전 미리 문제를 식별하고 수정할 수 있었으며, 리팩토링 시 안정성을 확보할 수 있었다.
5. 비동기 처리 최적화
Webflux와 Coroutine을 활용하여 모든 API가 Non-Blocking으로 동작하도록 구현하였다. 기존에 챗봇 프로젝트를 구현했었는데 동기적으로 처리되던 응답 방식을 비동기 처리로 전환함으로써, 응답 속도를 개선하고 동시 요청 처리 능력을 향상시켰다.
개선 후 느낀점
개선 작업을 함으로써 각 모듈과 계층이 명확하게 분리되어 독립성이 강화되었고, 관련 기능들이 한 모듈 내에 집중되어 응집도가 향상되었다. 또한 DIP와 인터페이스 기반 설계를 하여 모듈 간 결합도를 크게 줄여, 핵심 비즈니스 로직을 담은 도메인 계층이 외부 구현체와 기술 변화로부터 안전하게 보호되어 전체 시스템의 안정성과 유지보수성이 크게 향상되었다.
그리고 테스트 자동화를 통해 리팩토링 시 발생할 수 있는 부작용을 사전에 감지하고, 코드 변경이 빠르고 안전하게 이루어질 수 있었다.
이로 인해 개발 속도가 향상되고, 새로운 기능 추가나 수정에 대해 자신감이 붙을 수 있었다.
앞으로도 지속적인 개선과 새로운 기술 도입을 통해, 더욱 견고하고 유연한 시스템을 구축해 나가고 싶다.
'끄적끄적' 카테고리의 다른 글
MongoDB의 WiredTiger Engine과 Transaction에 대해서 알아보자 (2) | 2025.03.23 |
---|---|
TDD의 개념과 장단점 그리고 BDD by Kotest (4) | 2025.03.16 |

오늘은 써야지 라는 개발자 블로그 글쓰기 모임에 참여하게 되었다.
이 모임은 매주 개발 관련 블로그를 작성하고, 서로의 글을 공유하며 피드백을 나누고 경험과 인사이트를 나누자는 취지로 개설된 자리이다.
첫 주차의 주제는 '이제와서 고쳐보는 2024년의 내 코드'이다.
이번 포스팅은 2024년에 겪은 일들과 기존 코드의 문제점들과 한계점, 이를 어떻게 해결해나가야 할지, 앞으로 어떤 방향으로 나아갈 것인지에 대해 써보려 한다.
2024년은 큰 변화의 해
2024년은 내 개발 인생에 있어서 아래와 같은 큰 변화를 겪어, 큰 전환점이 있었던 해였다.
- TDD와 클린 아키텍처 도입
항해플러스를 통해 TDD를 더욱 깊게 공부하게 되었고, 클린 아키텍처 원칙을 적용해서 관심사를 분리하고 의존도가 낮은 시스템을 설계하는 방법을 배웠다. - 이직을 통한 기술 스택의 확장
실무 환경이 Java에서 Kotlin으로 전환하면서 문법의 간결함과 생산성을 체감하였다. 또한 NoSQL을 사용하기 시작하여 모델링의 유연성을 경험했고, Reactive 기반의 시스템을 Webflux와 Coroutine을 사용해 비동기 처리를 구현할 수 있게 되었다.
내 기존 코드들의 문제점과 한계점
새로운 기술과 방법론을 접하면서 지난 프로젝트들을 돌아보게 되었는데, 다음과 같은 한계점들이 눈에 띄게 드러났다.
- 구조적 복잡성
퇴근 후 짧은 시간씩 개발하다 보니 중복 코드와 모듈 간의 경계가 모호해진게 눈에 보였다. 작은 변경에도 사이드이펙트가 발생하는 상황들이 매우 빈번했다. - 테스트의 부재
TDD의 중요성을 깨닫기 전 작성된 코드들이라 기능 개선 시 디버깅과 리팩토링 과정에서 어려움이 있을 것이라 판단된다. - 3-Layered-Architecture의 한계
기존 코드는 presentation layer, business layer, data-access layer 3계층 구조로 구성되어 있지만, 이 아키텍처의 한계로 인해 유스케이스 로직과 서비스 로직이 명확하게 분리되지 않았다. - DIP 미적용에 따른 의존성 문제
기존 코드는 의존관계 역전 원칙(DIP)를 적용하지 않아, 외무 모듈과의 의존성이 지나치게 높았다. 이로 인해 외부 모듈의 변경이 실제 비즈니스 로직에 영향을 미치는 위험이 매우 컸다. - 기존 기술 스택의 한계
이직 전에는 Java, RDBMS, blocking 기반 시스템만 사용해서 개발해왔다. 이러한 기술 스택은 동시성 처리, 응답성 향상, 확장성 등 최신 요구사항에 부합하는 비동기 처리와 유연한 데이터 모델링 구현에 한계가 있음을 절실히 느끼게 했다.
실제로 기존 프로젝트 중 챗봇을 개발하던 것이 있었는데, Non-Blocking으로 구현을 해야 하던 것을 ReactiveMongo 또는 ReactiveRedis를 사용하는 법을 모른다는 이유로 급하게 동기 방식으로 DB 접근을 했던 기억이 있다.
그래서 어떻게 개선해야 할까?
문제점들을 인식한 다음, 다음과 같은 개선 방향을 설정하고 실행하였다.
1. 중복 제거와 모듈화
반복되는 로직들을 하나의 모듈로 통합해 재사용성을 높이고자 하였다. 여러 모듈이나 클래스에서 동일한 데이터 검증, 포맷 변환, 로깅 등의 작업이 반복적으로 작성된 경우를 발견하여 이러한 공통 기능을 별도의 유틸리티 클래스나 헬퍼 함수로 분리해, 각 모듈에서 해당 기능을 호출하도록 수정했다.
예를 들어, 문자열 포맷 변환이나 날짜 계산 로직이 여러 곳에 중복되어 있다면, DateUtils나 StringUtils 같은 공통 모듈을 만들어 한 번만 구현하고 재사용하도록 했다.
또한, 시스템 전반에 흩어져 있던 기능들이 단일 파일이나 모듈에 집중되어 있었기 때문에, 특정 기능의 변경이 다른 부분에 예기치 않은 영향을 미치고 있었는데, 이는 기능별로 책임을 명확히 나누어, 각 모듈이 독립적으로 동작하도록 리팩토링 작업을 하였다.
2. 명확한 계층 분리 및 DIP 적용
의존관계 역전을 이용하여 외부 모듈과의 결합도를 낮추어 각 모듈 간 경계를 명확하게 했다.
이 부분에 대해서 조금 더 자세히 설명을 하자면, 아래와 같은 방식으로 인터페이스의 특징을 이용해, 의존 관계를 거꾸로 하여 도메인 계층을 가장 안쪽으로 두어 보호할 수 있다는 것이다. 예를 들어 User의 RefreshToken을 DB에 넣고 있었는데, Redis에 넣도록 변경하고 싶다면 아래 그림과 같이 도메인 계층의 UserRepository 인터페이스는 수정할 필요 없이 구현체 부분에만 RedisTemplate를 사용하는 방식으로 변경해주면 되는 것이다.

3. Application 계층 도입
유연한 어플리케이션을 설계하기 위해선 객체 간의 협력 관계를 고려해야 한다. 이를 통해 각 객체들의 책임을 명확히 하고, 각 책임이 어떤 행동을 하게 할 것인지 고민해야 한다. 즉 도메인에 집중해야 한다.
그리하여 Application 계층을 도입하고 아래처럼 4-layered-Architecture로 전환을 하게 되었다.

4. 테스트 자동화 강화 (TDD, Kotest, MockK 도입)
기존에 Junit과 Mock/Stubbing 기법 대신, Kotlin 환경에서 더 자연스럽게 사용할 수 있는 Kotest와 MockK를 활용하여 테스트 코드를 작성했다. 아직은 조금 어색한 감이 있지만 기능 구현 전 테스트코드를 먼저 작성하고, 변경 사항 발생 시에도 안정적인 리팩토링을 수행할 수 있도록 하였다. 이렇게 작업을 하면서 단위 테스트와 통합테스트를 체계적으로 구현하였고, 기능 구현 전 미리 문제를 식별하고 수정할 수 있었으며, 리팩토링 시 안정성을 확보할 수 있었다.
5. 비동기 처리 최적화
Webflux와 Coroutine을 활용하여 모든 API가 Non-Blocking으로 동작하도록 구현하였다. 기존에 챗봇 프로젝트를 구현했었는데 동기적으로 처리되던 응답 방식을 비동기 처리로 전환함으로써, 응답 속도를 개선하고 동시 요청 처리 능력을 향상시켰다.
개선 후 느낀점
개선 작업을 함으로써 각 모듈과 계층이 명확하게 분리되어 독립성이 강화되었고, 관련 기능들이 한 모듈 내에 집중되어 응집도가 향상되었다. 또한 DIP와 인터페이스 기반 설계를 하여 모듈 간 결합도를 크게 줄여, 핵심 비즈니스 로직을 담은 도메인 계층이 외부 구현체와 기술 변화로부터 안전하게 보호되어 전체 시스템의 안정성과 유지보수성이 크게 향상되었다.
그리고 테스트 자동화를 통해 리팩토링 시 발생할 수 있는 부작용을 사전에 감지하고, 코드 변경이 빠르고 안전하게 이루어질 수 있었다.
이로 인해 개발 속도가 향상되고, 새로운 기능 추가나 수정에 대해 자신감이 붙을 수 있었다.
앞으로도 지속적인 개선과 새로운 기술 도입을 통해, 더욱 견고하고 유연한 시스템을 구축해 나가고 싶다.
'끄적끄적' 카테고리의 다른 글
MongoDB의 WiredTiger Engine과 Transaction에 대해서 알아보자 (2) | 2025.03.23 |
---|---|
TDD의 개념과 장단점 그리고 BDD by Kotest (4) | 2025.03.16 |