1. 프로젝트 생성
우선 스프링부트 프로젝트를 생성하고, 설정하는 부분부터 시작하자.
프로젝트는 다음의 설정으로 생성할것이다.
스프링부트 버전 | 3.5.0 |
Java 버전 | 17 |
빌드 도구 | Gradle (Groovy DSL) |
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.0'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.devQuest'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
2. 의존성
우선 기본적으로 생각나는 의존성들을 세팅해줬다. 데이터베이스는 우선 개발용으로 H2를 사용하는것으로 해서 의존성을 추가했고 MySql의 경우 추후 추가하겠다. 이 의존성은 기능개발에 따라 추가될수있다. (ex) Oauth의존성)
spring-boot-starter-web | 웹개발 |
spring-boot-starter-data-jpa | JPA |
spring-boot-starter-security | 보안 |
spring-boot-starter-websocket | WebSocket 지원 |
io.jsonwebtoken:jjwt-api | JWT API |
io.jsonwebtoken:jjwt-impl | JWT 내부 구현 |
io.jsonwebtoken:jjwt-jackson | Jackson 기반 JWT JSON 처리 |
spring-boot-starter-test | Spring 테스트 |
junit-platform-launcher | JUnit |
springdoc-openapi-starter-webmvc-ui:2.5.0 | Swagger UI |
lombok | lombok |
spring-boot-devtools | 자동 재시작 |
com.h2database:h2 | 개발용 DB |
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
implementation "org.springframework.boot:spring-boot-starter-security"
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
}
3. 디렉토리 구조
이번 프로젝트는 작업을하며 기능을 하나하나 새로 추가해 나갈것이기 때문에 도메인간의 의존성을 최대한 줄이고자 Domain 중점의 디렉토리 구조를 생성하였다. 도메인 관련 부분은 domain 디렉토리에서 생성하고, 그외의 요소들은 global 디렉토리에서 생성한다.
C:.
├─domain
│ ├─auth
│ │ ├─controller
│ │ ├─dto
│ │ │ ├─requestDto
│ │ │ └─responseDto
│ │ ├─model
│ │ ├─repository
│ │ └─service
│ └─member
│ ├─controller
│ ├─dto
│ │ ├─requestDto
│ │ └─responseDto
│ ├─model
│ ├─repository
│ └─service
└─global
├─components
├─config
├─exception
├─jwt
└─listener
4. 설정
이제 본격적으로 프로젝트의 설정을 해보자
4-1. SecurityConfig
우선 지금 SpringBoot Security 때문에 모든 엔드포인트에 보안이 걸려있으므로 제일 먼저 이거에 관한 설정을 하자.
global/config/SecurityConfig.java 파일을 생성해준다. 그후 이제 내부에 @Bean으로 설정빈들을 추가할것이다.
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
}
우선 가장먼저 보안 필터 체인을 설정해준다. .requestMatchers("/**").permitAll()로 우선 개발용이기에 모든 엔드포인트에 대한 접근권한부터 허용해주자. 그리고 지금 프로젝트에서 SpringBoot서버는 Rest API 방식이므로 불필요한 CSRF 보호를 비활성화해줬고, 서버가 클라이언트의 세션 상태를 저장하지 않도록하기위해 sessionMangement 설정또한 해준다.
headers 설정은 사실 Rest 방식에서 필요없지만 지금 h2데이터베이스를 webUI상으로 볼것이기 때문에 추가해준다.
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/**")
.permitAll()
.anyRequest().authenticated())
.csrf(csrf -> csrf.disable())
.headers(headers -> headers.frameOptions(frame -> frame.sameOrigin()))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
두번째로는 CORS설정이다. setAllowCredentials(true)로 인증또한 허용해줬다.
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration
.setAllowedOrigins(List.of("http://localhost:5173", "http://localhost:8080", "http://127.0.0.1:3000"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
전체 SecurityConfig.java 코드
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/**")
.permitAll()
.anyRequest().authenticated())
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration
.setAllowedOrigins(List.of("http://localhost:5173", "http://localhost:8080", "http://127.0.0.1:3000"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
4-2. H2 설정
h2 데이터베이스 설정이다. application.properties에 H2 관련 설정들을 추가해준다.
h2 데이터베이스 파일은 내 사용자루트디렉토리에 local3.mv.db 파일을 경로로한다.
#H2
#copy con local3.mv.db 으로 만들기
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:~/local3
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
local3.mv.db파일 또한 명령어로 빠르게 생성해주자
4-3.Swagger-UI 설정
지금도 Swagger 문서를 볼수있지만 개발 편의성을 위해 SwaggerConfig 파일을 만들어서 설정을하자
Swagger에서도 JWT로 인증한뒤에 엔드포인트에 접근할수있고 간단한 문서 이름들을 설정해줬다.
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("DevQuest API")
.version("1.0")
.description("DevQuest API 문서"))
.addSecurityItem(new SecurityRequirement().addList("BearerAuth")) // JWT 인증 설정
.components(new io.swagger.v3.oas.models.Components()
.addSecuritySchemes("BearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")));
}
}
설정후 Authorize버튼으로 인증이 가능하게 되었다.
application.properties에 그리고 다음 설정을 추가해서 페이지가 새로고침될때마다 JWT인증이 날아가서 다시 입력하는 번거로움을 제거하자
springdoc.swagger-ui.persistAuthorization=true
4-4.JPA 설정
application.properties에 JPA 사용을 위한 설정들을 추가하였다.
우선 지금은 H2로 개발용이기 때문에 H2 데이터베이스로 jpa를 설정했다.
# JPA
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
5.예외처리
이제 프로그램을 진행하며 예외를 던지면 받아서 처리해주는 GlobalExceptionHandler클래스를 만들자.
이제 앞으로 커스텀예외나 예외를 던지게 되면 여기에서 처리를 위한 부분을 작성할것이다.
예외가 발생하면 Client에게 문제가 생긴 부분에 대해 응답해줘야한다.
@Hidden
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleUnexpectedError(Exception ex) {
log.error("Unexpected error: {}", ex.getMessage());
String apiResponse = "Unexpected error: " + ex.getMessage();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(apiResponse);
}
}
6.로그단계 설정
resources/logback.xml 파일을 생성해서 로그에 대한 설정을 할것이다.
개발과정에서 오류가 하나나면 너무 깊게 로그가 올라와서 일일히 볼수없기때문에 로그 형식과 깊이,유형을 설정해줬다.
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 로그 출력 포맷-->
<pattern>[%-5level] %date{yyyy-MM-dd HH:mm:ss} [%logger{36}] - %msg%n</pattern>
</encoder>
</appender>
<!-- 로그 레벨 설정 -->
<root level="ERROR">
<appender-ref ref="CONSOLE" />
</root>
<!-- Spring Boot 로그 -->
<logger name="org.springframework" level="ERROR"/>
<!-- 패키지의 로그 -->
<logger name="com.example" level="ERROR" />
</configuration>
<!--ERROR INFO DEBUG-->
이제 스프링부트도 실행하면 이것만 딱 log가 나온다. (Spring Boot 로그레벨을 ERROR로 설정해서)
7.StartupHealthCheckListener
위에서 로그레벨을 ERROR로 설정해서 SpringBoot가 잘 실행됬는지 확인할수없으니 이를 위해 Listner 클래스를 하나 생성해주자. 이 클래스는 추후에 Redis나 다른 요소들또한 체크해서 프로젝트 시작시 잘 실행되었는지 확인할것이다.
@Component
@Log4j2
@RequiredArgsConstructor
public class StartupHealthCheckListener implements ApplicationListener<ApplicationReadyEvent> {
Instant startTime = Instant.now();
@Override
public void onApplicationEvent(@NonNull ApplicationReadyEvent event) {
checkStartupTime();
}
// Spring Boot 시작 시간
private void checkStartupTime() {
Instant endTime = Instant.now();
long timeTaken = Duration.between(startTime, endTime).toMillis();
System.out.println("[스프링부트 정상실행]: " + timeTaken + " 밀리초");
}
}
이제 스프링부트 실행이 안정적으로 되었는지 확인가능하다.
현 디렉토리
C:.
│ devQuestApplication.java
│
├─domain
└─global
├─components
├─config
│ SecurityConfig.java
│ SwaggerConfig.java
│
├─exception
│ GlobalExceptionHandler.java
│
├─jwt
└─listener
StartupHealthCheckListener.java
'프로젝트 > DevQuest' 카테고리의 다른 글
#3 인증 및 회원구현(2)- Redis 추가 및 로그아웃, 회원 엔드포인트 (1) | 2025.06.19 |
---|---|
#2 인증 및 회원구현(1)- JWT, 로그인 회원가입 구현 (1) | 2025.06.18 |
#0 프로젝트 개요 및 구성도 (2) | 2025.06.17 |