(MSA)3.auth 모듈개발
Spring Boot JWT 인증 서버 구성하기 (Hexagonal Architecture + MySQL)
이번 글에서는 Spring Boot
기반으로 JWT 인증 서버를 구축하는 방법을 정리합니다.
아키텍처는 헥사고날 아키텍처(Hexagonal Architecture) 패턴을 따르며, DB는 MySQL
, 빌드 도구는 Gradle
을 사용합니다.
이 구조는 확장성과 테스트 편의성이 뛰어나며, 도메인 → 유스케이스 → 어댑터로 흐름이 깔끔하게 나뉘어 유지보수가 매우 좋습니다.
📦 패키지 구조
com.enjoywalk.auth
│
├── adapter # 외부 시스템과의 연결 (입력/출력 어댑터)
│ ├── in # 외부에서 들어오는 요청 처리 (ex. Controller)
│ │ └── web # REST API 컨트롤러 위치
│ │ └── mapper # Request/Response <-> DTO 변환용 매퍼
│ └── out # 외부로 나가는 처리 (DB, 외부 API 등)
│ └── persistence # 데이터베이스 접근 어댑터
│ ├── entity # JPA 엔티티 클래스
│ └── mapper # Entity <-> Domain 모델 매핑
│
├── application # 유스케이스 계층 (비즈니스 로직 흐름 처리)
│ ├── port # 의존성 역전 지점 (인터페이스 기반)
│ │ ├── in # 유스케이스 입력 포트 (ex. 서비스 인터페이스)
│ │ └── out # 유스케이스 출력 포트 (ex. DB 호출 인터페이스)
│ └── service # 유스케이스 구현체 (비즈니스 흐름 담당)
│
├── domain # 도메인 계층 (핵심 비즈니스 모델과 로직)
│ ├── model # 도메인 모델 (ex. Member, Token 등)
│ └── service # 도메인 정책/로직 처리 (순수 비즈니스 로직)
│
├── infrastructure # 기술적 세부 구현 (보안, 설정, JWT 등)
│ ├── config # 보안 설정, CORS, Bean 설정 등
│ ├── filter # 커스텀 필터 (ex. JWT 인증 필터)
│ └── jwt # JWT 토큰 생성 및 검증 로직
│
└── AuthApplication.java # 스프링 부트 메인 실행 클래스
설명
mapper
패키지가adapter
의 in/out 구간에서 각각의 패키지로 구분되도록 구성infrastructure
패키지 추가해서 기술적 세부 구현을 분리
⚙️ Gradle 설정 (build.gradle)
plugins {
id 'java'
id 'org.springframework.boot'
id 'io.spring.dependency-management'
}
dependencies {
implementation project(':common') // common 모듈 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' // JSON 처리용
}
🛠️ application.yml 설정
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
sql:
init:
mode: always
schema-locations: classpath:h2\schema.sql
decorator:
datasource:
p6spy:
enable-logging: false # H2일 땐 P6Spy 굳이 끌 수도 있어요
jwt:
secret: "4c6Vb@tLq9kZ$J6sB3rU^7u8yA0M2tX2b3LzJ1tQG9y1kB5aV&yP7sVn3zB8xW"
expiration: 86400000
✅ logback-spring.xml 설정 예시
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 콘솔 출력 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- P6Spy SQL 로그 전용 로거 -->
<logger name="p6spy" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<!-- 전체 루트 로그 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
🔐 Spring Security 설정 (SecurityConfig)
package com.enjoywalk.auth.infrastructure.config;
import com.enjoywalk.auth.infrastructure.filter.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable) // CSRF 비활성화
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // 모든 요청에 대해 JWT 인증 필터 추가
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
🔁 구성 흐름 요약
[Request]
→ Adapter In (Web: Controller)
→ Application Port In (UseCase Interface)
→ Application Service (유스케이스 구현체)
→ Domain Model / Domain Service
→ Application Port Out (Repository Interface)
→ Adapter Out (JPA, 외부 API 등)