우선 웹페이지 회원가입, 로그인 기능 부터 만드려고 한다.
사용자는 어머니 한 명이지만, 내 개인 공부 목적과 향후 확장성을 고려해 Spring Security jwt 방식을 사용해서 구현할 것이다.
- Maven
- oepnJDK - 21
- spring boot - 3.5.6

프로젝트는 이렇게 세팅했다.
그리고 pom.xml에 JWT DI 추가해주자.
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
유튜버 개발자 유미님의 강의 영상을 참고 했습니다.
Spring Security JWT의 동작 원리이다.

1) 외부에서 POST 요청으로 폼 데이터가 들어온다.
2) Controller 에서 DTO 타입으로 받고 Service의 회원가입 프로세스를 호출한다.
3) 프로세스에서 문제가 없으면 Service에서 User Entity를 저장한다.

1) 외부에서 로그인 요청이 들어온다.
2) AuthenticationFilter 회원 검증
3) UserRepository가 DB에서 USER 데이터를 가져온다.
4) DB 데이터를 UserDetailsService가 UserDetails로 반환한다.
5) Authentication Manager가 로그인 검증.
6) successfulAuth의 메소드를 통해 JWTUtil에서 토큰을 발급.

1) 경로 요청이 들어온다.
2) Security Authentication Filter가 검증을 한다.
3) JWT Filter가 토큰을 검증한다.
4) SecurityContextHolderSession 일시적 요청에 대한 Session을 생성한다.
5) 생성된 세션은 요청이 끝나면 소멸된다.
웹을 구현할 때 어떤 요청이 있으면 회원 권한이 있는 사람만 데이터를 보여줘야 하기 때문에 회원 기능은 필수적이다.
사용자가 서버에 아이디와 패스워드를 넘기고 인증을 마치면 사용자에게 입장권을 준다.
이제부터 어떤 요청에 대해서 항상 이 입장권을 체크하게 된다.
입장권에 간단한 내용만 적혀있다.
일련 번호가 있고 유효 기간은 서버 메모리나 DB에 저장되어 확인할 수 있다.
입장권에 내용이 상세하다. 이름, 이메일, 유효 기간 등..
이때 서버는 유효 기간과 다른 인증에 문제가 없으면 된다.
jwt의 장점은 stateless 하다는 것인데.
만약 동시 접속자가 많다면 session 방식의 경우 조회가 힘들어지는 상황이 발생한다.
jwt는 입장권 처리만 잘 하면 되니 방식이 간단하다.
좀 더 자세한 이론은 나중에 따로 정리 해야겠다.
application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://주소/DB_이름
spring.datasource.username=아이디
spring.datasource.password=비밀번호
spring.security.user.name=admin // 임시 아이디
spring.security.user.password=1234 // 임시 비밀번호
spring.security.user.roles=USER // 임시 역할
MainController.java
package com.example.classicHub.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MainController {
@GetMapping("/")
public String mainP() {
return "main page";
}
}
api 서버를 구현중이니 @RestController 에노테이션을 추가 했다.
SecurityConfig.java
package com.example.classicHub.config;
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.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() { // Spring Security 인증/인가는 항상 비밀번호를 HASH로 암호화하여 진행한다.
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
// csrf disable
http.csrf((auth) -> auth.disable());
// Form 로그인 방식 disable
http.formLogin((auth) -> auth.disable());
// http basic 인증 방식 disable
http.httpBasic((auth) -> auth.disable());
// 경로별 인가 작업
http.authorizeHttpRequests((auth) -> auth
.requestMatchers("/login","/","/join").permitAll() // 이 경로에 대해서는 모든 권한을 허가한다.
.requestMatchers("/admin").hasRole("ADMIN") // 이 경로에 대해서는 해당 권한이 있는 사용자만 접근을 허가한다.
.anyRequest().authenticated() // 그 외 경로에는 로그인된 사용자만 권한을 허가한다.
);
// 세션 설정
http.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) // JWT를 통한 인증/인가를 위해서 세션을 STATELESS 상태로 설정.
);
return http.build();
}
}
이렇게 해두면 완전히 api 서버용으로 로그인 페이지가 뜨지 않는다.