1장 · 스프링 부트 & 레이어드 아키텍처
백엔드의 큰 그림부터 잡습니다. 스프링 부트?가 무엇인지, 빈·의존성 주입?은 어떻게 동작하는지, 코드가 어떤 층?으로 나뉘고 한 요청이 어떻게 흐르는지.
스프링 부트란?
Java로 백엔드 서버를 만드는 표준 프레임워크입니다. 서버 구동·DB 연결·보안 같은 복잡한 설정을 자동으로(auto-configuration) 잡아줘서, 우리는 업무 코드에 집중할 수 있어요. 이 프로젝트는 Spring Boot 3.3.1 / Java 17, 포트 8084에서 돕니다.
main 실행) 바로 달립니다.// study-backend/src/main/java/gsc/study/StudyApplication.java
@SpringBootApplication // 자동 설정 + 컴포넌트 스캔을 한 번에 켜는 표시
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args); // 서버 시작
}
}빈 & 의존성 주입 (DI)
스프링이 만들어 관리하는 객체를 빈(Bean)이라 합니다. 어떤 클래스가 다른 객체(예: UserRepo)가
필요하면 직접 new 하지 않고, 스프링이 대신 넣어줍니다(의존성 주입).
보통 생성자 주입을 쓰며, Lombok @RequiredArgsConstructor가 final 필드용 생성자를 자동으로 만들어 줍니다.
@RestController
@RequiredArgsConstructor // final 필드를 받는 생성자 자동 생성 → 생성자 주입
@RequestMapping("/api/users")
public class UserAccountEndpoint {
private final UserRepo userRepo; // 스프링이 주입
private final PasswordEncoder passwordEncoder;
}스프링이 객체의 생성·연결·생명주기를 관리하면, 테스트할 때 가짜 객체로 바꿔 끼우거나(모킹) 구현을 교체하기 쉬워집니다. 이것이 제어의 역전(IoC)입니다.
패키지 구조
코드는 역할별로 나뉩니다. 루트 패키지는 com.example.study.
| 패키지 | 역할 | 예시 |
|---|---|---|
config/ | 설정 (보안·QueryDSL·감사·Jackson) | SecurityConfig, WebSecurityConfig |
core/model/entity/ | 엔티티? (DB 테이블 매핑) | User, Role, Menu |
core/repo/ | 저장/조회/삭제 (Command) | UserRepo, RoleRepo |
core/query/ | 복잡한 조회 (QueryDSL?) | UserRepoQueryImpl |
core/service/ | 업무 로직 (이 프로젝트는 얇음) | JwtTokenProvider |
shared/ | 공통 베이스·예외·유틸 | BaseAuditEntity, BizRuleException |
web/endpoint/ | REST 컨트롤러? + 전역 예외 | UserAccountEndpoint |
web/dto/ | 요청/응답 DTO? | UserCreateReq, UserDTO |
web/filter/ | HTTP 필터 (JWT 인증) | JwtAuthenticationFilter |
레이어드 아키텍처 & 요청 흐름
일반적으로 백엔드는 컨트롤러 → 서비스 → 리포지토리로 책임을 나눕니다.
이 프로젝트는 한발 더 나아가 읽기/쓰기를 분리한 흔적이 있어요 — 쓰기·기본 CRUD는 core/repo(Command),
복잡한 조회는 core/query(Query)로 나눕니다.
// shared/base/BaseRepo.java — "Command와 Query를 구분하기 위해 crud만 제공"
@NoRepositoryBean
@EnableJpaRepositories(queryLookupStrategy = QueryLookupStrategy.Key.USE_DECLARED_QUERY)
public interface BaseRepo<T, ID> extends CrudRepository<T, ID> { }"컨트롤러 → 서비스 → 리포지토리" 3계층이 정석이지만, 이 학습 프로젝트는 서비스 층이 얇아서
UserAccountEndpoint·RoleEndpoint는 서비스를 거치지 않고 리포지토리를 직접 호출합니다.
개념은 3계층으로 이해하되, 코드에선 서비스가 생략된 곳이 많다는 점을 기억하세요.
한 요청이 거치는 길 (개요)
HTTP 요청 → JwtAuthenticationFilter (web/filter) : JWT 검증 → 인증정보 저장 → SecurityFilterChain (config) : 공개/인증 경로 판단 → @RestController (web/endpoint): @Valid로 요청 DTO 검증 → core/repo (Command) 또는 core/query (QueryDSL) : DB 접근 → 엔티티 → 응답 DTO 변환 (DTO.fromEntity) → JSON 응답 (예외 발생 시 → GlobalExceptionHandler → 표준 에러 응답)
→ 이 흐름을 프론트까지 이어 단계별로 보기: 연동 1장 · 전체 요청 흐름
- 새 기능 추가 — 보통 엔티티 → 리포지토리 → (서비스) → 컨트롤러 → DTO 순으로 한 층씩 만든다
- 버그 추적 — 화면 문제면 컨트롤러/DTO, 데이터 문제면 리포지토리/쿼리 층부터 본다
- 역할 분리 — 단순 저장은
core/repo, 검색·필터·페이징은core/query
설정 파일 살펴보기
실행 옵션은 application.properties, 의존성·빌드는 build.gradle에 있습니다.
# application.properties (발췌) server.port=8084 spring.datasource.url=jdbc:postgresql://localhost:5432/studydb?currentSchema=lds spring.liquibase.change-log=classpath:/db/changelog/changelog-index.yaml # 스키마는 Liquibase가 관리 app.jwt.expirationMs=43200000 # 액세스 토큰 12시간 app.jwt.refreshExpirationMs=604800000 # 리프레시 7일
build.gradle에는 JPA·QueryDSL·jOOQ·MyBatis가 모두 있지만, 실제로 쿼리하는 코드는 JPA + QueryDSL뿐입니다. jOOQ는 코드 생성까지만, MyBatis는 설정만 되어 있어요(회사 스택을 따라 자리만 마련). 자세히는 4장에서.