-
Notifications
You must be signed in to change notification settings - Fork 97
[그리디] 김민욱 SpringCore 7~9단계 제출합니다. #274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
hapdaypy
wants to merge
69
commits into
next-step:hapdaypy
Choose a base branch
from
hapdaypy:hapdaypy5
base: hapdaypy
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
69 commits
Select commit
Hold shift + click to select a range
89c3a69
feat: 토큰 생성 및 검증 기능 추가
mgim9316-a11y 1e6f4cf
feat: 로그인 API 구현
mgim9316-a11y c348327
feat: 서버의 토큰 발급 확인 테스트 코드
mgim9316-a11y 29152d3
refactor: resorver가 토큰을 받는 형태로 수정
mgim9316-a11y f084866
refactor: resorver가 토큰을 받는 형태로 수정
mgim9316-a11y da4f4ec
refactor: 예약 생성 시 ReservationReqeust의 name이 없는 경우 Cookie에 담긴 정보를 활용
mgim9316-a11y 0853489
fix: 2단계 테스트 코드 오류 수정
mgim9316-a11y 40bb963
커밋테스트
mgim9316-a11y 0c8710c
커밋테스트
mgim9316-a11y 0a5c76b
커밋테스트
mgim9316-a11y cdaddb5
커밋테스트
mgim9316-a11y 50c51c3
커밋테스트
hapdaypy 76ce709
refactor: JWT 생성/검증 책임 클래스 분리
hapdaypy 5b27027
refactor: 네이밍 수정 및 로그인한 회원의 정보를 담는 객체 분리
hapdaypy 7479e06
refactor: 네이밍 수정 및 로그인한 회원의 정보를 담는 객체 분리
hapdaypy bd2b014
refactor: 쿠키 관련 로직을 분리
hapdaypy 4aff64a
refactor: simpledbcInsert로 변경
hapdaypy b2e568f
refactor: ReservationDao의 조건 조회를 NamedParameterJdbcTemplate 로 바꿈
hapdaypy 66ce159
feat: “내 예약” 화면이 호출하는 API
hapdaypy 506eceb
feat: 공통 mapper 메서드를 추가
hapdaypy fbadb3f
refactor: 로그인 실패 조건식 수정
hapdaypy 446bd84
feat: 에러 응답 메시지 추가
hapdaypy 5870f17
refactor: 애노테이션을 옵션으로 분기하지 않고, Optional로 선택 가능성 표현
hapdaypy a862274
refactor: 인증 패키지 분리
hapdaypy d3c5540
refactor: 가독성을 위해 코드 분리
hapdaypy 68ab241
refactor: AuthUser, AdminOnly 권한 분리
hapdaypy 765d7a4
refactor: 인텁센터 로직 수정
hapdaypy b207c49
feat: 예외 처리를 상태코드로 나눔
hapdaypy ce3992d
refactor: DB 조회 데이터가 0개일 경우 에러를 NotFoundException 로 반환
hapdaypy cc91e2a
feat: 생성 멱등성을 검증하는 로직 추가
hapdaypy dbf7630
feat: 일반 사용자 기능과 관리자 기능을 인텁셉터를 도입하여 분리
hapdaypy 6a6e810
refactor: controller의 예외 처리 로직을 service로 반경
hapdaypy da6a387
refactor: jjwt의 JwtParser 사용
hapdaypy 654fff7
refactor: 사용자의 role을 enum으로 관리
hapdaypy 319ac52
feat: JWT access/refresh token 발급 흐름 추가
hapdaypy c9f3185
refactor: DTO 성격을 record 형태로 변경 및 record 호출 형태로 수정
hapdaypy 24416b3
refactor: raw type 을 구체적 타입으로 수정
hapdaypy bace53d
refactor: record 형태로 수정
hapdaypy 87aca69
fix: cherry-pick conflict cleanup
hapdaypy 41e1d3a
refactor:수동 getter를 @Getter 로 정리
hapdaypy 8374a94
feat: @Transactional 사용
hapdaypy 8c10705
feat: @Entity로 바꿈
hapdaypy 737af23
refactor: EntityManager 기반으로 교체
hapdaypy f6b104e
feat: 테마 기능 서비스 생성
hapdaypy 74b7d5e
refactor: 컨트롤러에서 DAO, 서비스 의존성 제거
hapdaypy 8aebdb7
refactor: optional 추가
hapdaypy 34599e8
refactor: 예약과 회원을 JPA 연관관계로 매핑
hapdaypy e0ac584
refactor: 상속하는 파일들 패키지로 분리
hapdaypy e0bca6d
refactor: 에러메시지를 enum으로 관리
hapdaypy 8a2b3fd
refactor: 사용되지 않은 생성자 삭제
hapdaypy 2387c0f
refactor: RuntimeException 전체를 잡아 AuthenticationException으로 변환하는 부분 수정
hapdaypy 45bb709
feat: 토큰 파싱 실패시, AuthenticationException으로 반환
hapdaypy 815c598
refactor: @Value로 secret 주입받아 key를 만들도록 변경
hapdaypy d914306
refactor: isValidSignature()를 제거
hapdaypy 255dcae
feat: 이메일 컬럼 매핑 추가
hapdaypy 0c00db4
refactor: getSingleResult()에서 발생하는 예외 사용
hapdaypy f764dd3
refactor: 회원이 없을 때 예외를 던지지 않고Optional<Member>를 반환
hapdaypy 6759fac
refactor: 서비스에서 실제 엔티티를 조회한 뒤 DAO에 넘기도록 변경
hapdaypy b45a1b0
refactor: join과 DTO projection 을 사용하도록 리팩토링
hapdaypy 39fa068
refactor: id를 받는 생성자 삭제
hapdaypy c6d7687
merge: sync hapdaypy base
hapdaypy 20eed98
refactor: EntityManager 기반 DAO를 Spring Data JPA로 전환
hapdaypy 07d08eb
feat: 예약 대기자 기능 추가
hapdaypy 1e6d264
refactor: JwtUtils를 roomescape 외부의 jwt 패키지로 이동
hapdaypy 99ba6d9
refactor: schema.sql을 프로파일별 데이터 로더로 전환
hapdaypy 34ae942
refactor: 사용되지 않은 코드 정리
hapdaypy 280d7fc
docs: 리드미 작성
hapdaypy 8101c4b
feat: 액세스 토큰 재발급 시 회원 정보 조회
hapdaypy a28299c
chore: 주석 삭제
hapdaypy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| # 방탈출 예약 관리 | ||
|
|
||
| 이 저장소는 Spring MVC와 JPA를 공부하며 만든 방탈출 예약 서비스입니다. JDBC와 직접 작성한 DAO로 시작해 Spring Data JPA, 로그인 인증, 예약 대기 기능을 차례로 붙였습니다. 학습 단계마다 코드를 고쳐 온 흔적이 남아 있어 기능이 늘면서 구조가 어떻게 달라졌는지 살펴볼 수 있습니다. | ||
|
|
||
| ## 주요 기능 | ||
|
|
||
| - 회원가입과 로그인 | ||
| - Access Token과 Refresh Token을 사용한 인증 | ||
| - 관리자 전용 예약, 테마, 시간 관리 | ||
| - 예약 생성과 멱등성 키를 이용한 중복 요청 방지 | ||
| - 내 예약 목록 조회 | ||
| - 예약 대기 등록과 대기 순번 조회 | ||
| - 테마와 시간의 소프트 삭제 | ||
| - 프로파일에 따른 초기 데이터 등록 | ||
|
|
||
| ## 기술 스택 | ||
|
|
||
| - Java 17 | ||
| - Spring Boot 3.1 | ||
| - Spring MVC | ||
| - Spring Data JPA | ||
| - H2 Database | ||
| - JWT | ||
| - Thymeleaf | ||
| - Gradle | ||
| - RestAssured, JUnit 5, AssertJ | ||
|
|
||
| ## 프로젝트 구조 | ||
|
|
||
| ```text | ||
| src/main/java | ||
| ├── jwt JWT 생성과 파싱 | ||
| └── roomescape | ||
| ├── auth 인증, 인가, 쿠키 처리 | ||
| ├── exception 예외와 오류 코드 | ||
| ├── loader 실행 환경별 초기 데이터 | ||
| ├── member 회원 | ||
| ├── reservation 예약 | ||
| ├── theme 테마 | ||
| ├── time 예약 시간 | ||
| └── waiting 예약 대기 | ||
| ``` | ||
|
|
||
| 패키지는 기능 단위로 나눴습니다. 컨트롤러는 HTTP 요청과 응답을 받고 서비스는 유스케이스와 트랜잭션을 처리합니다. 데이터 접근은 `JpaRepository`를 상속한 저장소가 맡습니다. | ||
|
|
||
| ## 실행 방법 | ||
|
|
||
| Java 17이 필요합니다. 데이터베이스를 따로 설치하지 않아도 H2 인메모리 데이터베이스로 실행할 수 있습니다. | ||
|
|
||
| Windows: | ||
|
|
||
| ```bash | ||
| gradlew.bat bootRun | ||
| ``` | ||
|
|
||
| macOS 또는 Linux: | ||
|
|
||
| ```bash | ||
| ./gradlew bootRun | ||
| ``` | ||
|
|
||
| 실행 후 [http://localhost:8080](http://localhost:8080)으로 접속하면 됩니다. H2 콘솔 주소는 [http://localhost:8080/h2-console](http://localhost:8080/h2-console)입니다. | ||
|
|
||
| 기본 계정은 다음과 같습니다. | ||
|
|
||
| | 구분 | 이메일 | 비밀번호 | | ||
| | --- | --- | --- | | ||
| | 관리자 | `admin@email.com` | `password` | | ||
| | 사용자 | `brown@email.com` | `password` | | ||
|
|
||
| ## 초기 데이터 | ||
|
|
||
| 초기 데이터는 `schema.sql`이 아니라 `CommandLineRunner`로 등록합니다. | ||
|
|
||
| - 기본 환경에서는 `DataLoader`가 회원 정보를 등록합니다. | ||
| - `test` 프로파일에서는 `TestDataLoader`가 회원, 테마, 시간, 예약 데이터를 등록합니다. | ||
|
|
||
| 테이블은 JPA 엔티티를 보고 Hibernate가 생성합니다. 현재 H2 인메모리 데이터베이스와 `create-drop` 설정을 사용하므로 애플리케이션을 다시 실행하면 데이터도 초기화됩니다. | ||
|
|
||
| ## API | ||
|
|
||
| ### 회원과 인증 | ||
|
|
||
| | Method | URI | 설명 | | ||
| | --- | --- | --- | | ||
| | `POST` | `/members` | 회원가입 | | ||
| | `POST` | `/login` | 로그인 | | ||
| | `POST` | `/token/refresh` | Access Token 재발급 | | ||
| | `GET` | `/login/check` | 로그인 상태 확인 | | ||
| | `POST` | `/logout` | 로그아웃 | | ||
|
|
||
| ### 예약과 대기 | ||
|
|
||
| | Method | URI | 설명 | | ||
| | --- | --- | --- | | ||
| | `GET` | `/reservations` | 전체 예약 조회 | | ||
| | `POST` | `/reservations` | 예약 생성 | | ||
| | `DELETE` | `/reservations/{id}` | 예약 삭제 | | ||
| | `GET` | `/reservations-mine` | 내 예약과 예약 대기 조회 | | ||
| | `POST` | `/waitings` | 예약 대기 등록 | | ||
|
|
||
| ### 테마와 시간 | ||
|
|
||
| | Method | URI | 설명 | | ||
| | --- | --- | --- | | ||
| | `GET` | `/themes` | 테마 목록 조회 | | ||
| | `POST` | `/themes` | 테마 생성 | | ||
| | `DELETE` | `/themes/{id}` | 테마 삭제 | | ||
| | `GET` | `/times` | 시간 목록 조회 | | ||
| | `POST` | `/times` | 시간 생성 | | ||
| | `DELETE` | `/times/{id}` | 시간 삭제 | | ||
| | `GET` | `/available-times` | 날짜와 테마별 예약 가능 시간 조회 | | ||
|
|
||
| 예약, 테마, 시간 관리 API 일부는 관리자 권한이 필요합니다. | ||
|
|
||
| ## 구현하면서 바꾼 점 | ||
|
|
||
| ### Spring Data JPA | ||
|
|
||
| 직접 `EntityManager`를 다루던 DAO를 `JpaRepository`로 바꿨습니다. 단순 조회에는 파생 쿼리를 씁니다. 여러 엔티티를 조인해 DTO로 반환하는 예약 조회에는 JPQL 생성자 표현식을 사용했습니다. | ||
|
|
||
| ### 소프트 삭제 | ||
|
|
||
| 테마와 시간은 행을 바로 지우지 않고 `deleted` 상태만 바꿉니다. 예약을 생성할 때는 삭제되지 않은 테마와 시간만 조회합니다. | ||
|
|
||
| ### 예약 대기 순번 | ||
|
|
||
| 같은 날짜, 테마, 시간에 먼저 등록된 대기 건수를 JPQL 서브쿼리로 계산합니다. 조회된 건수에 1을 더해 `1번째 예약대기`처럼 응답합니다. | ||
|
|
||
| ### JWT | ||
|
|
||
| JWT 라이브러리를 직접 다루는 코드는 `roomescape` 패키지 밖의 `JwtUtils`에 모았습니다. 이 클래스에는 컴포넌트 어노테이션을 붙이지 않고 `JwtConfig`에서 빈으로 등록했습니다. 토큰에서 회원 식별 정보와 권한을 읽기 때문에 토큰 확인 과정에서는 회원 데이터베이스를 다시 조회하지 않습니다. | ||
|
|
||
| ## 테스트 | ||
|
|
||
| Windows: | ||
|
|
||
| ```bash | ||
| gradlew.bat test | ||
| ``` | ||
|
|
||
| macOS 또는 Linux: | ||
|
|
||
| ```bash | ||
| ./gradlew test | ||
| ``` | ||
|
|
||
| 테스트는 `test` 프로파일로 실행됩니다. 로그인, 예약 생성, 멱등성 키, 예약 대기 순번, JWT 설정을 REST API 기준으로 확인합니다. | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package jwt; | ||
|
|
||
| import io.jsonwebtoken.Claims; | ||
| import io.jsonwebtoken.Jwts; | ||
| import io.jsonwebtoken.SignatureAlgorithm; | ||
| import io.jsonwebtoken.security.Keys; | ||
|
|
||
| import java.nio.charset.StandardCharsets; | ||
| import java.security.Key; | ||
| import java.util.Date; | ||
| import java.util.Map; | ||
|
|
||
| public class JwtUtils { | ||
| private final Key signingKey; | ||
|
|
||
| public JwtUtils(String secretKey) { | ||
| this.signingKey = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); | ||
| } | ||
|
|
||
| public String createToken(String subject, Map<String, Object> claims, long expirationTime) { | ||
| Date now = new Date(); | ||
| Date expiration = new Date(now.getTime() + expirationTime); | ||
|
|
||
| return Jwts.builder() | ||
| .setSubject(subject) | ||
| .addClaims(claims) | ||
| .setIssuedAt(now) | ||
| .setExpiration(expiration) | ||
| .signWith(signingKey, SignatureAlgorithm.HS256) | ||
| .compact(); | ||
| } | ||
|
|
||
| public Claims parseToken(String token) { | ||
| return Jwts.parserBuilder() | ||
| .setSigningKey(signingKey) | ||
| .build() | ||
| .parseClaimsJws(token) | ||
| .getBody(); | ||
| } | ||
| } | ||
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기는 따로 빈 등록을 하지 않은 이유가 있나요??