Spring Boot

[Spring] 효율적인 패키지 구조와 네이밍 컨벤션

kittity 2025. 3. 27. 23:05

목차

  1. 패키지 구조의 중요성과 기본 원칙
  2. 스프링 프로젝트 초기 세팅을 위한 패키지 구조 가이드
  3. 네이밍 컨벤션과 예시
  4. 도메인 중심 vs 계층 중심 구조
  5. 다양한 패키지 구성 예시

 

1. 패키지 구조의 중요성과 기본 원칙

패키지 구조는 단순한 코드 정리 이상의 의미를 갖는다. 잘 설계된 패키지 구조는 코드의 가독성을 높이고, 유지보수를 용이하게 하며, 개발자 간 협업을 효율적으로 만든다. 스프링 프로젝트에서 패키지 구조가 중요한 이유는 다음과 같다.

 

1.1 코드 탐색성 향상

개발자가 코드를 처음 접할 때, 패키지 구조는 프로젝트의 전체 구조를 이해하는 첫 번째 단서가 된다. 논리적이고 명확한 패키지 구조는 새로운 개발자가 빠르게 프로젝트에 적응할 수 있게 도와준다.

1.2 의존성 관리

패키지는 의존성 관리의 기본 단위다. 잘 설계된 패키지 구조는 순환 의존성을 방지하고, 모듈 간의 관계를 명확히 한다.

1.3 기본 패키지 구조 원칙

  • 단일 책임 원칙(SRP): 하나의 패키지는 관련된 책임을 모아둔 응집도 높은 구성이어야 한다.
  • 캡슐화: 패키지 내부 구현은 가능한 한 숨기고, 필요한 인터페이스만 노출한다.
  • 패키지 크기 적정화: 너무 작거나 너무 큰 패키지는 오히려 가독성을 떨어뜨린다.

 

2. 스프링 프로젝트 초기 세팅을 위한 패키지 구조 가이드

스프링 프로젝트를 시작할 때 기본적인 패키지 구조 설정은 프로젝트의 방향성을 결정짓는 중요한 작업이다.

2.1 루트 패키지 네이밍

루트 패키지는 일반적으로 도메인 역순으로 시작하며, 프로젝트 또는 회사명을 포함한다.

com.company.project
io.github.username.project
org.organization.project

예를 들어, example.com 도메인을 사용하는 회사라면 com.example로 시작하는 패키지 네이밍을 사용한다.

 

2.2 기본 계층 구조

스프링 프로젝트의 초기 세팅 시 일반적으로 다음과 같은 계층 구조를 고려한다.

com.example.project
├── config        // 설정 클래스들
│   ├── security  // 보안 관련 설정
│   └── web       // 웹 관련 설정
├── domain        // 도메인 모델
├── repository    // 데이터 접근 계층
├── service       // 비즈니스 로직
├── web           // 컨트롤러 등
│   ├── controller    // REST 컨트롤러
│   ├── dto           // 데이터 전송 객체
│   └── exception     // 웹 계층 예외 처리
└── util          // 유틸리티 클래스들

 

2.3 스프링부트 애플리케이션 클래스 위치

스프링부트 애플리케이션 클래스는 모든 컴포넌트 스캔이 가능하도록 루트 패키지에 위치시킨다.

package com.example.project;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProjectApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProjectApplication.class, args);
    }
}

 

3. 네이밍 컨벤션과 예시

패키지와 클래스의 네이밍 컨벤션은 코드의 가독성과 유지보수성에 직접적인 영향을 미친다.

3.1 패키지 네이밍 규칙

  • 패키지명은 모두 소문자를 사용한다.
  • 단어 구분을 위한 밑줄(_)이나 대문자를 사용하지 않는다.
  • 약어 사용을 최소화하고 의미가 명확한 이름을 사용한다.
좋은 예: com.example.user.management
나쁜 예: com.example.UserManagement, com.example.user_management

 

3.2 클래스 네이밍 패턴

스프링 프로젝트에서 자주 사용되는 클래스 네이밍 패턴은 다음과 같다.

컨트롤러 [도메인명]Controller UserController
서비스 [도메인명]Service UserService
리포지토리 [도메인명]Repository UserRepository
엔티티 [도메인명] User
DTO [도메인명][용도]Dto UserResponseDto
설정 [기능]Configuration SecurityConfiguration

 

3.3 인터페이스와 구현체 네이밍

인터페이스와 그 구현체의 네이밍은 다음과 같은 패턴을 따른다.

  • 인터페이스: [기능]Service, [기능]Repository
  • 구현체: [기능]ServiceImpl, [기능]RepositoryImpl
// 인터페이스
public interface PaymentService {
    void processPayment(Payment payment);
}

// 구현체
public class PaymentServiceImpl implements PaymentService {
    @Override
    public void processPayment(Payment payment) {
        // 구현 내용
    }
}

 

4. 도메인 중심 vs 계층 중심 구조

스프링 프로젝트에서는 계층 중심(layered) 구조와 도메인 중심(domain) 구조로 크게 두 가지 패키지 구조 패턴이 사용된다. 

4.1 계층 중심 구조

계층 중심 구조는 애플리케이션의 기술적 계층에 따라 패키지를 구성한다.

com.example.project
├── controller
│   ├── UserController
│   └── ProductController
├── service
│   ├── UserService
│   └── ProductService
├── repository
│   ├── UserRepository
│   └── ProductRepository
└── domain
    ├── User
    └── Product

장점

  • 기술 계층이 명확히 구분되어 계층별 책임이 명확하다.
  • 계층별 공통 관심사를 쉽게 관리할 수 있다.

단점

  • 하나의 기능 변경 시 여러 패키지를 수정해야 한다.
  • 프로젝트 규모가 커지면 패키지 내 클래스가 너무 많아진다.

 

4.2 도메인 중심 구조

도메인 중심 구조는 비즈니스 도메인에 따라 패키지를 구성한다.

com.example.project
├── user
│   ├── User
│   ├── UserController
│   ├── UserService
│   └── UserRepository
├── product
│   ├── Product
│   ├── ProductController
│   ├── ProductService
│   └── ProductRepository
└── common
    └── util

장점

  • 관련 기능이 모두 한 패키지에 모여 있어 응집도가 높다.
  • 기능 변경 시 해당 도메인 패키지만 수정하면 된다.
  • 도메인의 경계가 명확해 마이크로서비스로의 전환이 용이하다.

단점

  • 계층 간 공통 관심사 관리가 어려울 수 있다.
  • 도메인 간 경계가 모호할 경우 패키지 구조가 복잡해질 수 있다.

 

4.3 선택 기준

  • 프로젝트 규모: 작은 규모는 계층 중심, 큰 규모는 도메인 중심이 유리
  • 팀 구성: 기술 중심 팀은 계층 중심, 도메인 중심 팀은 도메인 중심이 유리
  • 변경 빈도: 도메인별 독립적 변경이 많다면 도메인 중심 구조가 유리

 

5. 다양한 패키지 구성 예시

실무에서는 프로젝트의 성장과 확장을 고려한 패키지 구성이 필요하다.

5.1 멀티모듈 프로젝트 구조

대규모 프로젝트에서는 기능별로 모듈을 분리하는 멀티모듈 구조를 고려할 수 있다.

com.example.project
├── project-api        // 외부 API 제공 모듈
├── project-admin      // 관리자 기능 모듈
├── project-batch      // 배치 처리 모듈
├── project-core       // 핵심 비즈니스 로직 모듈
└── project-common     // 공통 기능 모듈

5.2 아키텍처별 패키지 구조

프로젝트의 아키텍처에 따라 패키지 구조도 달라질 수 있다. 여기서는 대표적인 아키텍처 패턴에 따른 패키지 구조를 간략히 살펴보자.

 

[레이어드 아키텍처]

전통적인 N-계층 아키텍처로, 단방향 의존성을 가진 계층 구조이다.

 

com.example.project
├── presentation        // 프레젠테이션 계층 (컨트롤러)
├── service             // 비즈니스 로직 계층 (서비스)
├── persistence         // 데이터 접근 계층 (리포지토리)
└── domain              // 도메인 모델

레이어드 아키텍처는 이해하기 쉽고 구현이 단순하다는 장점이 있지만, 하위 계층에 대한 의존성으로 인해 테스트가 어렵고 변경에 취약할 수 있다.

 

[클린 아키텍처]

로버트 C. 마틴이 제안한 클린 아키텍처는 의존성 방향이 내부(도메인)로 향하는 구조이다.

클린 아키텍처는 핵심 비즈니스 로직을 외부 요소로부터 독립시켜 테스트와 유지보수가 용이하지만, 작은 프로젝트에서는 과도한 추상화로 복잡도가 증가할 수 있다.

 

[헥사고날 아키텍처]

포트와 어댑터 패턴으로도 알려진 헥사고날 아키텍처는 외부 시스템과의 통신을 포트(인터페이스)를 통해 추상화한다.

 

 

 

패키지 구조와 네이밍 컨벤션은 프로젝트의 시작부터 완성, 그리고 이후의 유지보수까지 영향을 미치는 중요한 요소다. 초기에 잘 설계된 패키지 구조는 프로젝트가 성장함에 따라 발생할 수 있는 복잡성을 효과적으로 관리하는 데 도움이 된다. 

 

프로젝트의 특성, 팀의 구성, 비즈니스 요구사항에 맞게 패키지 구조를 선택하고, 일관된 네이밍 컨벤션을 적용함으로써 더 유지보수하기 쉽고 확장 가능한 스프링 애플리케이션을 구축할 수 있다.

 

728x90