F-Lab
🚀
취업/이직이 고민이신가요? 합격에 필요한 모든 것을 도와드립니다.
🚀
취업/이직이 고민이신가요? 합격에 필요한 모든 것을 도와드립니다.

요구사항 변화에 따른 프로젝트 구조 확장_Bradley 멘토님

writer_thumbnail

F-Lab : 상위 1% 개발자들의 멘토링

안녕하세요! 11 년차 백엔드 개발자이자 멘토 Bradley입니다.


멘티분들이 프로젝트를 시작 할 때  확장성과 유연함을 강조하지만, 실제로는 Spring mvc나 RDBMS 와 같은 특정 기술에 맞춰 패키지 구조를 설계하는 경우가 많습니다. 이는  확장성과 유연함을 제한하는 방식이죠. 그래서 이번 글에서는 멘티분들의 방식과는 조금 다른 방식으로, 핵심 요구사항에 집중하고, 추가 요구사항에 따라 프로젝트 구조를 확장해 나가는 경험을 공유하고자 합니다. 이를 통해 핵심 요구사항에 집중하는 과정에서 자연스럽게 확장성과 유연성을 갖춘 프로젝트 구조가 어떻게 만들어졌는지 그 경험을 공유해 드리겠습니다.


 

프로젝트의 시작


“이거 쉽게 계산 할 수 있게 해 줘.”

어느 주말, 파생상품 가치 평가(금융 관련 도메인)를 공부하던 회계사인 배우자(이하 배우자)가 저에게 갑작스러운 요구를 해 왔습니다. 처음에는 갑작 스러운 요구에 당황했지만, 계산 관련 로직이라 그리 어렵지 않을 거라 생각했습니다. 그래서 가벼운 마음에 배우자의 상황과 이에 따른 요구사항을 이해하기 위해 대화를 나누어 보았고, 내용은 다음과 같습니다.
 

실무에서의 문제점

구체적인 요구사항을 파악하기 전에 현재 배우자가 처한 어려움을 정리 해 보았습니다.

 

요구사항 

문제점을 통해 이야기 해 본결과 배우자의 요구사항은 단 하나였습니다. 이 외에는 다른 어떤 요구사항도 없었습니다.


개발자 입장에서의 구현 방식 혹은 서비스 제공 방식 등에 대한 고민 거리는 배우자에겐 그닥 중요한게 아니였습니다. 아래는 11년차 서버 개발자 입장에서의 추후 필요한 기능에 대한 문의와 그에 대한 배우자의 답변입니다.

 

 

핵심 요구사항 구현 

지금까지의 개발 경험은 주로 웹 서버를 중심으로 이루어져 있었습니다. 프로젝트를 시작 하기 전에 항상 프레임워크와 데이터베이스를 먼저 선택했습니다. 하지만 이번 프로젝트의 초기에는 핵심 요구사항을 충족하기 위한 핵심 로직 외에는 정해진 것이 아무것도 없었습니다. 배우자의 요구사항을 충족하기 위해 필요 한 것은 POJO(Plain Old Java Object) 로 구성 된 도메인 로직 뿐 이였습니다. 오히려 이러한 접근 방식이 부차적인 것에 대한 고민을 줄이고 서비스가 제공하려는 것의 핵심에만 집중 할 수 있었습니다.

핵심 기능에만 집중한 결과 도메인 로직은 복잡한 설계 없이 없이 단순하게 개발 되었습니다. 공통으로 사용되는 로직인 Logger 와 공통 자료구조, 수학 계산 등을 위한 유틸 함수 및 클래스는 따로 패키지를 생성해 관리하긴 했지만, 이외에는 모든 클래스는 Domain 내부에 존재 했습니다.

당시의 프로젝트 구조는 아래와 같습니다. 핵심 비지니스 로직이 존재하는 Domain, 로깅을 위한 Logger, 각종 유틸 함수 들을 위한 Util 로 구성되어 있습니다. 이외의 ProgramMain 함수는 프로젝트의 진입점 입니다.

다음은 스톡옵션 계산을 위한 Domain 패키지 내부의 간략한 클래스 구조입니다. 다소 생소하게 느껴질 수 있는 금융 도메인 설계이므로, 각 클래스의 구체적인 역할보다는 클래스들이 패키지 내부에 캡슐화되어 있다는 점에 주목해 주시기 바랍니다.


 

사용성 개선과 구조 확장

핵심 로직이 어느정도 동작 되는 것을 확인한 후, 구현된 프로그램이 실제 업무에 도움이 될 것을 직감한 배우자는 사용성 개선을 통해 실제 업무 적용에 관심을 갖기 시작 했습니다. 프로그래밍에 대한 배경 지식이 전혀 없는 배우자는 ProgramMain 클래스에 직접 값을 입력하고 빌드 하는 것에 대해 큰 진입장벽을 느끼고 있었습니다. 이를 해결하기 위해 배우자와 저는 사용성 개선에 대한 논의를 진행했습니다.
 

추가 요구사항

위의 요구사항을 해결 하기 위해 아래의 인터페이스 개선 방안(1차)을 제안 하였고 승인 되었습니다.
 

인터페이스 개선 - 1차

  • 엑셀 파일을 통한 인터페이스
  • 사용자는 특정 폴더에 미리 정해진 형식의 엑셀 파일을 배치
  • 프로그램은 엑셀 파일을 읽어 그 내용에 따라 평가 로직을 수행

 

개선안에 따른 추가 기능

위의 요구사항들을 충족 하기 위해 추가되어야 하는 기능들은 아래와 같습니다.

  • 특정 폴더에서 Excel 파일을 읽어와 명령어를 생성
  • 명령어에 맞는 동작을 실행
     

개선안을 반영한 구조 설계

추가 기능에 대한 설계를 반영한 구조는 아래와 같습니다.

 만약 ProgramMain class 에 위의 추가 동작을 전부 구현한다면 ProgramMain 은 너무 많은 책임을 지게 되어 SRP 를 위반하게 됩니다. 이를 피하기 위해, CommandParser 와 FunctionRouter 를 통해 Excel 파일의 파싱과 명령어 매핑 로직을 분산하였습니다.

또한, Application 패키지를 추가 함으로써 비지니스 로직의 진입점의 역할을 하도록 설계 하였습니다. 이 패키지는 프로그램이 제공하는 서비스(유스케이스)를 외부에 노출 시키고 해당 서비스 동작을 위한 도메인간 상호작용을 오케스트레이션 하는 역할을 부여 했습니다. 이를 통해 비지니스 로직에 대한 상세 구현을 외부로 부터 격리시킬 수 있는 구조로 작성하였습니다.
 

아래는 Application 패키지의 내부 클래스 구조를 간단하게 표현한 다이어그램입니다.

이 구조에서 주목해야 할 점은 Function Router 의 의존성 방향은 Application 패키지를 향한다는 것입니다. 이를 위해 Application 내부에 있는 Service 의 파라미터나 리턴을 위한 객체들은 Application 패키지 내부에 존재 해야 합니다. Application 패키지 안에 있는 Service 들은 서비스를 제공하기 위해 여러 도메인들 간의 상호작용을 중재하고, 이를 통해 비즈니스 요구사항을 충족 하도록 설계되었습니다. 

 

접근성 개선을 위한 인터페이스 추가

인터페이스 개선 - 1차 를 통해 개선된 인터페이스는 일종의 임시방편이였습니다. cmd 나 특정 폴더를 활용하는 방법은 PC의 환경 설정에 따라 제대로 작동하지 않을 수 있습니다. 특히 jar 파일 같은 경우 윈도우에서는 악성 코드로 오인 될 가능성도 존재 합니다. 이는 배우자가 근무하는 환경에서 실행 할 때 보안상 문제가 생길 여지가 있습니다. 이를 해결하기 위해 배우자와 저는 다시 회의를 소집하여 대응방안을 고심하였습니다.
 

인터페이스 개선 - 2차

여러 장단점을 검토한 결과, 웹 인터페이스가 가장 적절하다고 판단하여 이를 활용한 서비스 제공을 결정했습니다. 특히 다양한 플랫폼에서 쉽게 접근할 수 있다는 점이 결정에 큰 영향을 미쳤습니다. 웹 인터페이스는 범용성 측면에서 최적의 선택이었고, 추후 추가 요구사항이 생길 경우를 고려했을 때 확장성 또한 뛰어났습니다. 또한, 저에게 익숙한 영역이라 러닝 커브 없이 빠른 개발을 기대할 수 있었습니다. 다만, 백엔드가 아닌 프론트엔드의 경우 학부생 시절 과제로 진행해 본 경험이 전부여서 어려움이 예상되었으나, 결과적으로 UI는 AI를 활용해 구현할 수 있었습니다.
 

개선안에 따른 추가 요구사항

앞서 설명한 구조에서 보시는 바와 같이, 비즈니스 로직 부분은 Application 패키지의 진입점을 통해 격리되어 있습니다. 따라서 웹 인터페이스가 추가되더라도 핵심 비즈니스 로직을 담고 있는 패키지 내부의 코드는 변경할 필요가 전혀 없습니다. 더욱이 Application과 Domain 패키지에는 이미 충분한 테스트가 작성되어 있어, 외부 인터페이스의 어떠한 변경에도 핵심 로직의 안정적인 동작을 보장합니다. 이는 곧 변화하는 요구사항에 유연하게 대응할 수 있다는 의미입니다.
 

개선안을 반영한 구조 설계

기존의 엑셀 지원을 위한 클래스들을 모두 제거하고, 웹 인터페이스 제공을 위한 API 패키지를 추가했습니다. 아래는 이를 반영한 구조입니다. 참고로, 이 프로젝트에서는 API 부분을 Spring Framework를 사용하여 구현했습니다.

이전 섹션에서 설명했던 의존성 방향(Function Router가 Application Package를 의존) 덕분에, Application Package의 변경 없이 Function Router를 Web Controller로 변경하여 웹 인터페이스를 지원할 수 있었습니다.


아래는 이를 반영한 구조입니다.

Web 패키지는 외부와의 통신을 위한 모든 기능을 담당합니다. 이러한 구조 덕분에 추후 인증 기능이나 API 호출 수 제한 등 웹 관련 기능이 추가되더라도 비즈니스 로직은 전혀 영향을 받지 않습니다. 또한, 웹 인터페이스가 아닌 윈도우 애플리케이션을 구현하더라도 API 패키지 대신 GUI에 해당하는 로직을 통해 애플리케이션을 호출하면 되므로, 외부 인터페이스의 요구사항에 매우 유연하게 대응할 수 있습니다.

 

검증 데이터 파일 관리를 위한 저장소 추가

신규 구현한 서비스를 통해 평가한 결과와 기존의 회사에서 평가한 결과가 허용 오차 범위 내에서 일치하였기 때문에 우리는 이 프로젝트의 기본적인 신뢰성이 확보 되었다고 판단했습니다. 하지만 테스트 케이스 숫자 자체가 적기 때문에 추가 검증이 필요 하다고 판단했습니다. 또한 타인이 서비스를 이용 했을 때 결과값을 신뢰할 수 있는지는 다른 문제였습니다. 따라서 결과 값으로 부터 계상 중간 과정을 역으로 추적 할 수 있도록 하기 위해 검증 데이터 파일 제공 기능을 추가하기로 하였습니다.
 

검증 데이터 파일 제공 기능 요구사항

서비스 사용자들은 대부분 엑셀로 업무를 처리하는 특성이 있습니다. 따라서 검증 데이터 파일은 사용자들에게 익숙한 엑셀 파일 형식으로 제공됩니다. 회계 감사에도 사용될 수 있는 데이터인 만큼, 사용자가 입력한 정보 중에는 대외비에 해당하는 내용이 있을 수 있습니다. 검증 데이터 파일은 사용자가 입력한 정보도 포함하고 있기 때문에, 보안상의 이유로 일정 시간이 지나면 삭제되어야 합니다. 위 내용을 정리하면 다음과 같습니다.

 

요구사항 분석

검증 데이터 파일은 평가 요청 시 생성되는 평가 결과 데이터와는 다른 생명주기를 갖고 있습니다. 평가 결과 데이터는 요청에 대해 응답 한 뒤 소멸되지만, 검증 데이터 파일은 별개의 다운로드 요청 시 응답으로 제공 됩니다. 아래는 검증 데이터 파일의 생명주기 입니다.

표의 내용을 종합 해 봤을 때 검증 데이터 파일 의 생명주기를 관리하고 별도의 다운로드 요청에 파일로 제공하기 위해서는 영속성이 필요하다고 판단 되었습니다.
 

영속성을 적용한 구조 설계

영속성을 위한 데이터베이스가 핵심 로직에 영향을 주지 않도록 설계를 진행했습니다. 이를 위해 비즈니스 로직을 담고 있는 `Application` 및 `Domain` Package 에 각각 `Repository` 인터페이스를 정의하고, 외부 솔루션에 연동하는 Data Package 내부에 Application 및 Domain 에 Repository 인터페이스의 실제 구현체를 두어 의존성 주입(DI)을 통해 데이터베이스 솔루션 선택에 따른 핵심 로직 변경이 없도록 구조 설계를 진행했습니다.
 

다음은 영속성이 적용된 Data Package 와 Repository 인터페이스가 추가 된 Application Package 의 클래스 다이어그램입니다.

위 다이어그램에서 보듯이, `Repository` 인터페이스는 `Application` 패키지 내부에 정의되어 있습니다. 이는 검증데이터 파일은 핵심 도메인 로직이 아니라고 판단 했기 때문입니다. 만약 검증데이터 파일에 관련된 로직이 핵심 도메인 중 일부라면 `Domain` 패키지 내부에 `Repository` 인터페이스를 구현 하고, 그 구현체를 `Data` 패키지에 두는것도 자연스럽습니다.
 

 아래는 영속성까지 반영한 최종 구조 입니다.

위 구조의 핵심은 영속성 계층의 변경이 도메인 로직에 미치는 영향을 완전히 차단한다는 점입니다. 
의존성 주입(DI)을 통해 영속성 계층의 의존성 방향이 Business 로직을 향하도록 설계함으로써,  RDBMS, NoSQL, 또는 외부 서비스 API 등 어떤 영속성 솔루션을 선택하더라도, 도메인 로직은 이에 전혀 영향을 받지 않습니다. 이러한 설계는 시스템의 유연성과 확장성을 크게 향상시키며, 기술 스택 변경에 대한 비용을 최소화합니다. 

 

마치며

“사이드 프로젝트에서 확장성을 고려한 구조는 과하다”는 의견을 종종 듣곤 합니다. 저 역시 현재 요구사항도 없는데 미래를 위해 복잡한 구조를 설계 하는 것은 불필요하다고 생각합니다. 하지만 이 글에서 제시한 방식은 다릅니다. 확장성을 목표로 삼아 처음부터 복잡한 구조를 설계하는 것이 아닙니다. 핵심 요구사항을 먼저 구현하고, 부차적인 요구사항들을 추가하는 과정에서 기존 코드에 미치는 영향을 최소화하기 위해 자연스럽게 격리가 일어나며 결과적으로 확장성 있는 구조가 만들어 지는 것입니다. 즉, 확장성은 목표가 아닌 부산물입니다.

만약 “웹 백엔드를 만들겠다” 는 명확한 방향이 정해져 있다면 다른 접근이 필요할 수 있습니다. 하지만 여러분이 만들고 싶은 핵심 기능 이 먼저 떠오른다면, 부수적인 요구사항들은 모두 제쳐두고 그 핵심부터 구현 해 보세요. 핵심을 먼저 구현하면, 확장성과 유연성은 자연스럽게 따라옵니다.

ⓒ F-Lab & Company

이 컨텐츠는 F-Lab의 고유 자산으로 상업적인 목적의 복사 및 배포를 금합니다.

조회수

멘토링 코스 선택하기

  • 코스 이미지
    Java Backend

    아키텍처 설계와 대용량 트래픽 처리 능력을 깊이 있게 기르는 백앤드 개발자 성장 과정

  • 코스 이미지
    Node.js Backend

    아키텍처 설계와 대용량 트래픽 처리 능력을 깊이 있게 기르는 백앤드 개발자 성장 과정

  • 코스 이미지
    Python Backend

    대규모 서비스를 지탱할 수 있는 대체 불가능한 백엔드, 데이터 엔지니어, ML엔지니어의 길을 탐구하는 성장 과정

  • 코스 이미지
    Frontend

    기술과 브라우저를 Deep-Dive 하며 성능과 아키텍처, UX에 능한 개발자로 성장하는 과정

  • 코스 이미지
    iOS

    언어와 프레임워크, 모바일 환경에 대한 탄탄한 이해도를 갖추는 iOS 개발자 성장 과정

  • 코스 이미지
    Android

    아키텍처 설계 능력과 성능 튜닝 능력을 향상시키는 안드로이드 Deep-Dive 과정

  • 코스 이미지
    Flutter

    네이티브와 의존성 관리까지 깊이 있는 크로스 플랫폼 개발자로 성장하는 과정

  • 코스 이미지
    React Native

    네이티브와 의존성 관리까지 깊이 있는 크로스 플랫폼 개발자로 성장하는 과정

  • 코스 이미지
    Devops

    대규모 서비스를 지탱할 수 있는 데브옵스 엔지니어로 성장하는 과정

  • 코스 이미지
    ML Engineering

    머신러닝과 엔지니어링 자체에 대한 탄탄한 이해도를 갖추는 머신러닝 엔지니어 성장 과정

  • 코스 이미지
    Data Engineering

    확장성 있는 데이터 처리 및 수급이 가능하도록 시스템을 설계 하고 운영할 수 있는 능력을 갖추는 데이터 엔지니어 성장 과정

  • 코스 이미지
    Game Server

    대규모 라이브 게임을 운영할 수 있는 처리 능력과 아키텍처 설계 능력을 갖추는 게임 서버 개발자 성장 과정

  • 코스 이미지
    Game Client

    대규모 라이브 게임 그래픽 처리 성능과 게임 자체 성능을 높힐 수 있는 능력을 갖추는 게임 클라이언트 개발자 성장 과정

logo
copyright © F-Lab & Company 2025