Spring Boot 프로젝트 구조

Spring Initializr로 프로젝트를 생성하면 기본적으로 아래와 같은 구조가 만들어집니다. 개발 환경 설정과 프로젝트 생성 흐름은 Spring Boot 개발 환경 설정을 먼저 보면 이해하기 쉽습니다.

text
demo/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/demo/
│   │   │       └── DemoApplication.java
│   │   └── resources/
│   │       ├── application.properties
│   │       ├── static/
│   │       └── templates/
│   └── test/
│       └── java/
│           └── com/example/demo/
│               └── DemoApplicationTests.java
├── build.gradle
└── settings.gradle

처음 보면 폴더가 많아 보이지만, 크게 보면 다음과 같이 나눌 수 있습니다.

  • src/main/java: 실제 애플리케이션 코드
  • src/main/resources: 설정 파일과 정적 리소스
  • src/test/java: 테스트 코드
  • build.gradle: 프로젝트 빌드 및 의존성 설정

src/main/java

src/main/java는 Java 소스 코드를 작성하는 위치입니다. Spring Boot 애플리케이션의 컨트롤러, 서비스, 레포지토리, 도메인 클래스 등이 이 폴더 아래에 위치합니다.

예를 들어 기본 패키지 이름을 com.example.demo로 만들었다면, 다음 경로에 코드가 생성됩니다.

text
src/main/java/com/example/demo/

Spring Boot에서는 보통 메인 클래스가 있는 패키지를 기준으로 하위 패키지의 컴포넌트를 자동으로 탐색합니다. 그래서 특별한 이유가 없다면 DemoApplication.java와 같은 메인 클래스 아래에 기능별 패키지를 만드는 것이 좋습니다.

text
com/example/demo/
├── DemoApplication.java
├── controller/
├── service/
├── repository/
├── domain/
└── dto/

각 패키지의 역할은 다음과 같습니다.

패키지역할
controllerHTTP 요청을 받고 응답을 반환
service비즈니스 로직 처리
repository데이터베이스 접근
domain핵심 도메인 객체 또는 Entity
dto요청/응답 데이터를 전달하는 객체

DemoApplication.java

Spring Boot 프로젝트를 생성하면 DemoApplication.java 파일이 만들어집니다. 이 파일은 애플리케이션의 시작점입니다.

java
package com.example.demo;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class DemoApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
 
}

main 메서드에서 SpringApplication.run()을 호출하면 Spring Boot 애플리케이션이 실행됩니다.

여기서 중요한 것은 @SpringBootApplication 어노테이션입니다. 이 어노테이션은 아래 기능들을 합쳐놓은 어노테이션입니다.

  • @SpringBootConfiguration: Spring Boot 설정 클래스임을 나타냄
  • @EnableAutoConfiguration: Spring Boot의 자동 설정 기능 활성화
  • @ComponentScan: 현재 패키지와 하위 패키지에서 컴포넌트 탐색

즉, DemoApplication.java가 있는 패키지를 기준으로 Spring Bean을 찾기 때문에, 일반적으로 이 클래스는 프로젝트의 최상위 패키지에 두는 것이 좋습니다. Spring이 객체를 생성하고 연결하는 원리는 IoC와 DI 이해하기를 함께 보면 자연스럽게 이어집니다.

src/main/resources

src/main/resources는 Java 코드가 아닌 설정 파일과 리소스를 관리하는 위치입니다.

text
src/main/resources/
├── application.properties
├── static/
└── templates/

application.properties

application.properties는 Spring Boot 애플리케이션의 설정을 작성하는 파일입니다. 설정 파일의 문법과 환경별 분리는 application.yml과 application.properties 설정에서 더 자세히 다룹니다.

properties
server.port=8080
spring.application.name=demo

위 설정은 서버 포트를 8080으로 지정하고, 애플리케이션 이름을 demo로 설정합니다.

설정이 많아지면 application.yml 파일을 사용할 수도 있습니다.

properties
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=password

같은 설정을 application.yml로 작성하면 계층 구조로 표현할 수 있습니다.

yaml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo
    username: root
    password: password

propertiesyml 중 하나만 사용해도 됩니다. 실무에서는 설정이 많아질수록 가독성이 좋은 yml을 많이 사용합니다.

static

static 폴더는 정적 파일을 넣는 위치입니다. 이미지, CSS, JavaScript 파일 등을 둘 수 있습니다.

text
src/main/resources/static/
├── images/
│   └── logo.png
├── css/
│   └── style.css
└── js/
    └── app.js

예를 들어 static/images/logo.png 파일을 넣으면 브라우저에서 아래 주소로 접근할 수 있습니다.

text
http://localhost:8080/images/logo.png

templates

templates 폴더는 서버 사이드 템플릿 파일을 넣는 위치입니다. Thymeleaf 같은 템플릿 엔진을 사용할 때 HTML 파일을 이곳에 둡니다.

text
src/main/resources/templates/
└── index.html

다만 REST API 서버만 만든다면 templates 폴더를 사용하지 않는 경우도 많습니다.

src/test/java

src/test/java는 테스트 코드를 작성하는 위치입니다.

text
src/test/java/com/example/demo/
└── DemoApplicationTests.java

Spring Initializr로 프로젝트를 만들면 기본 테스트 클래스가 생성됩니다.

java
package com.example.demo;
 
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
 
@SpringBootTest
class DemoApplicationTests {
 
    @Test
    void contextLoads() {
    }
 
}

@SpringBootTest는 Spring Boot 애플리케이션 컨텍스트가 정상적으로 로딩되는지 확인할 때 사용할 수 있습니다. 처음에는 비어 있는 테스트처럼 보이지만, 애플리케이션 설정에 문제가 있으면 이 테스트가 실패합니다.

build.gradle

build.gradle은 Gradle 빌드 설정 파일입니다. 프로젝트에서 사용할 플러그인, Java 버전, 의존성 등을 정의합니다.

groovy
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.3.0'
    id 'io.spring.dependency-management' version '1.1.5'
}
 
group = 'com.example'
version = '0.0.1-SNAPSHOT'
 
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}
 
repositories {
    mavenCentral()
}
 
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
 
tasks.named('test') {
    useJUnitPlatform()
}

주요 항목은 다음과 같습니다.

  • plugins: Java, Spring Boot 관련 플러그인 설정
  • repositories: 의존성을 다운로드할 저장소 설정
  • dependencies: 프로젝트에서 사용할 라이브러리 설정
  • tasks.named('test'): JUnit 5를 사용하도록 테스트 설정

새로운 라이브러리가 필요하면 보통 dependencies 블록에 추가합니다.

settings.gradle

settings.gradle은 Gradle 프로젝트의 이름과 멀티 모듈 설정을 관리하는 파일입니다.

groovy
pluginManagement {
    repositories {
        maven { url = 'https://repo.spring.io/milestone' }
        gradlePluginPortal()
    }
}
 
rootProject.name = 'demo'

단일 프로젝트에서는 대부분 rootProject.name 정도만 확인하면 됩니다. 멀티 모듈 프로젝트를 구성할 때는 이 파일에 하위 모듈을 추가합니다.

groovy
rootProject.name = 'demo'
 
include 'api'
include 'common'

처음 Spring Boot를 공부할 때는 settings.gradle보다 build.gradle을 더 자주 수정하게 됩니다.

실무에서 자주 사용하는 패키지 구조

작은 예제에서는 메인 클래스와 컨트롤러를 같은 패키지에 두어도 문제가 없습니다. 하지만 프로젝트가 커지면 역할별로 패키지를 나누는 것이 좋습니다.

text
com/example/demo/
├── DemoApplication.java
├── controller/
│   └── UserController.java
├── service/
│   └── UserService.java
├── repository/
│   └── UserRepository.java
├── domain/
│   └── User.java
└── dto/
    ├── UserCreateRequest.java
    └── UserResponse.java

예를 들어 사용자 목록을 조회하는 API를 만든다면 흐름은 다음과 같습니다.

text
UserController

UserService

UserRepository

Database

각 계층의 책임을 나누면 코드가 많아져도 유지보수하기 쉬워집니다.

  • Controller: 요청과 응답 처리에 집중
  • Service: 핵심 로직 처리에 집중
  • Repository: 데이터 조회와 저장에 집중
  • Domain: 비즈니스의 핵심 개념 표현
  • DTO: 외부로 주고받는 데이터 표현

실무에서 패키지를 나눌 때의 기준

처음부터 패키지를 너무 세밀하게 나누면 오히려 이동 비용이 커집니다. 작은 프로젝트에서는 controller, service, repository, domain, dto 정도로 시작하고, 기능이 커질 때 user, order, payment처럼 도메인 기준으로 한 번 더 나누는 편이 관리하기 쉽습니다.

text
com/example/demo/
├── user/
│   ├── controller/
│   ├── service/
│   ├── repository/
│   ├── domain/
│   └── dto/
└── order/
    ├── controller/
    ├── service/
    ├── repository/
    ├── domain/
    └── dto/

제가 프로젝트를 시작할 때는 먼저 역할 기준으로 단순하게 나누고, 특정 도메인의 파일이 많아지기 시작하면 도메인 기준 구조로 옮기는 편입니다. 중요한 것은 처음부터 완벽한 구조를 만드는 것이 아니라, 팀이 파일 위치를 예측할 수 있게 일관성을 유지하는 것입니다.

참고 자료

정리

Spring Boot 프로젝트 구조는 처음에는 복잡해 보이지만, 역할을 기준으로 보면 어렵지 않습니다.

  • src/main/java에는 실제 애플리케이션 코드를 작성합니다.
  • DemoApplication.java는 애플리케이션의 시작점입니다.
  • src/main/resources에는 설정 파일과 정적 리소스를 둡니다.
  • src/test/java에는 테스트 코드를 작성합니다.
  • build.gradle에는 플러그인, Java 버전, 의존성을 설정합니다.
  • 프로젝트가 커지면 controller, service, repository, domain, dto처럼 역할별로 패키지를 나누는 것이 좋습니다.

프로젝트 구조를 이해하면 이후에 Controller, Service, Repository, JPA, 테스트를 학습할 때 각각의 코드가 어디에 위치해야 하는지 자연스럽게 이해할 수 있습니다.