Spring Security 실습 프로젝트. docs/의 STEP 문서를 순서대로 따라가며 인메모리 계정 하나에서 시작해 DB 기반 회원 관리, 역할 기반 인가, 작성자 권한 검증, OAuth2 소셜 로그인까지 확장한다.
src/main의 코드는 이 문서들을 구현한 최종 상태다. 즉 한 파일이 여러 STEP에 걸쳐 자라난 경우, 저장소에는 마지막 STEP의 모습만 남아 있다.
[1] 회원 관리 [2] 인가 [3] 소셜 로그인
01 프로젝트 설정 → 06 인증 정보·화면 분기 → 10 OAuth2 원리
02 회원 엔티티 07 경로 인가(RBAC) 11 Google·Kakao 연동
03 비밀번호 해시 08 게시판 CRUD
04 DB 인증 연결 09 작성자 권한 검증
05 회원 가입·CRUD
각 STEP은 앞 STEP의 산출물을 전제로 하므로 순서대로 읽어야 한다. docs/00-overview.md가 전체 지도 역할을 한다.
| STEP | 문서 | 이 STEP에서 만들어지는 것 |
|---|---|---|
| 00 | 실습 개요 | (문서만) |
| 01 | 프로젝트 초기 설정 | application.yaml, 패키지 뼈대 |
| 02 | 회원 엔티티와 JPA Auditing | common/BaseTimeEntity, config/JpaAuditingConfig, user/Role, user/UserAccount, user/UserAccountRepository |
| 03 | 비밀번호 해시와 PasswordEncoder | config/SecurityConfig (PasswordEncoder 빈), BouncyCastle 의존성 |
| 04 | UserDetails로 DB 인증 연결 | user/CustomUserDetails, user/CustomUserDetailsService, config/InitialDataLoader |
| 05 | 회원 가입과 CRUD·논리 삭제 | user/dto/*, common/DuplicateUsernameException, user/UserAccountService, user/UserAccountController, user/AdminUserAccountController, signup.html, login.html, user-accounts/me.html, admin/user-accounts.html |
| 06 | 인증 정보 활용과 화면 분기 | templates/fragments/layout.html |
| 07 | 경로 단위 인가(RBAC) | config/SecurityConfig (필터 체인), user/ErrorPageController, error/403.html |
| 08 | 게시판 CRUD와 작성자 매핑 | board/Board, board/BoardRepository, board/dto/*, board/BoardService, board/BoardController, boards/*.html |
| 09 | 작성자 권한 검증과 예외 처리 | common/GlobalExceptionHandler, error/404.html, BoardService·BoardController·boards/detail.html 확장 |
| 10 | OAuth2 원리와 클라이언트 구성 | (개념·설정 이해) |
| 11 | Google·Kakao 소셜 로그인 | user/AuthProvider, user/CustomOAuth2User, user/CustomOAuth2UserService, UserAccount·CustomUserDetailsService·SecurityConfig·application.yaml 확장 |
아래 파일들은 저장소에 최종형만 있으므로, 중간 단계가 궁금하면 해당 STEP 문서를 봐야 한다.
| 파일 | 변화 |
|---|---|
config/SecurityConfig |
03 PasswordEncoder 빈 → 07 필터 체인·폼 로그인·403 → 11 oauth2Login |
user/UserAccount |
02 기본 회원 → 11 email·provider·providerId, registerSocial |
user/UserAccountRepository |
02 기본 조회 → 11 (provider, providerId) 조회 |
user/CustomUserDetailsService |
04 findByUsername → 11 findByUsernameAndProvider(LOCAL) |
board/BoardService·BoardController |
08 목록·작성·상세 → 09 수정·삭제와 작성자 검증 |
templates/boards/detail.html |
08 상세 표시 → 09 canEdit 수정·삭제 버튼 |
application.yaml |
01 DB·H2 콘솔 → 11 OAuth2 클라이언트 등록 |
문서 코드를 그대로 옮기지 않은 부분은 해당 위치에 // [문서와 차이] 주석으로 이유를 남겼다.
| 차이 | 위치 |
|---|---|
CSS·JS 미구현 (템플릿에서 stylesheet link 제외, class 속성은 유지) |
모든 템플릿 |
OAuth2 자격 증명에 기본값 부여 (${GOOGLE_CLIENT_ID:changeme}) |
application.yaml |
모델 속성명을 me→userAccount, users→userAccounts로 통일 |
UserAccountController, AdminUserAccountController |
signup·create 반환형을 void로 변경 |
UserAccountService, BoardService |
403 화면을 @RequestMapping으로 (forward된 POST가 405가 되는 문제) |
ErrorPageController |
| 중복 아이디·비밀번호 불일치를 500 대신 폼 오류로 처리 | UserAccountController |
관리자 시딩에 @Builder 사용 (getRoles()가 불변이라 문서 방식 불가) |
InitialDataLoader |
Java 17 · Spring Boot 4.1 · Spring Security 7 · Spring Data JPA · Thymeleaf · H2 · Lombok · Gradle
Boot 4.x에서 이름이 바뀐 스타터를 사용한다: spring-boot-starter-webmvc, spring-boot-starter-security-oauth2-client, spring-boot-h2console. 비밀번호 해시에 Argon2id를 쓰기 위해 org.bouncycastle:bcprov-jdk18on을 추가로 선언했다.