Загружаем каталог…
Загружаем каталог…
미니 배달 주문 서비스 프로젝트 회고 및 개념 정리 Spring Boot 4.1.1 · Java 21 · PostgreSQL · JWT 미니 배달 주문 서비스 회원가입부터 로그인, 메뉴, 주문, 결제까지 직접 구현하면서 Spring에서 배운 개념들을 실제 프로젝트에 적용해보는 과정 1. 프로젝트 구조 프로젝트는 도메인별로 user , menu , order , payment 를 나누고 각 도메인 내부에서 controller , service , repository , entity , dto 로 계층을 분리했다 3 Layer Architecture Controller ↓ Service ↓ Repository ↓ Database 각 계층의 역할을 분리하고 의존 방향을 한쪽으로 유지하는 구조 2. 회원가입 회원가입에서는 @RequestBody 를 통해 JSON 데이터를 받고 @Valid 를 사용해 요청 데이터를 검증 비밀번호는 BCrypt로 암호화한 후 저장 아이디와 이메일 중복의 경우 DuplicateException 을 사용해 409 Conflict 로 처리 @RequestBody HTTP Body로 전달된 JSON 데이터를 DTO 객체로 변환 @Valid @RequestBody SignupRequestDto requestDto Validation @Valid 를 사용하면 DTO에 설정한 검증 조건을 기준으로 요청 데이터 검증 검증에 실패하면 Controller 메서드 내부까지 들어오기 전에 예외 발생 3. Spring Security와 JWT 로그인과 인증은 Spring Security와 JWT를 사용 인증과 인가 Authentication : 사용자가 누구인지 확인하는 과정 Authorization : 인증된 사용자가 해당 기능에 접근할 수 있는지 확인하는 과정 JWT 인증 흐름 로그인 요청 ↓ JwtAuthenticationFilter ↓ 사용자 인증 ↓ JWT 발급 ↓ Response Header 이후 API 요청에서는 API 요청 ↓ JwtAuthorizationFilter ↓ JWT 검증 ↓ 사용자 정보 확인 ↓ Controller JwtAuthenticationFilter 는 로그인 시 JWT를 발급 하고, JwtAuthorizationFilter 는 요청마다 JWT를 검증 하는 역할 STATELESS 이번 프로젝트에서는 Session 대신 JWT를 사용하기 때문에 SessionCreationPolicy.STATELESS 설정 서버에 세션을 저장하지 않고 매 요청마다 전달된 JWT를 검증하는 방식 Client ↓ JWT Server ↓ JWT 검증 4. SecurityConfig에서 겪었던 문제 프로젝트를 진행하면서 HTTP 상태 코드가 예상과 다르게 반환되는 문제가 여러 번 발생 특히 403 Forbidden이 계속 발생하는 문제 가 있었음 /error Spring에서 예외가 발생하면 /error 로 다시 요청이 들어올 수 있는데, 이 경로가 Security 설정에 의해 막히면 원래 발생한 400이나 409 대신 403으로 응답되는 현상 발생 .requestMatchers("/error").permitAll() 따라서 /error 경로는 열어두는 것이 중요 Security 규칙 순서 Spring Security의 Request Matcher는 위에서부터 순서대로 적용 구체적인 규칙 ↓ 전체 규칙 예를 들어 GET /api/menus 를 인증 없이 허용하려면 anyRequest().authenticated() 보다 먼저 설정해야 함 5. 메뉴 기능 메뉴에서는 다음 기능을 구현 메뉴 등록 메뉴 전체 조회 메뉴 단건 조회 메뉴 수정 메뉴 삭제 메뉴 조회는 인증 없이 가능하도록 설정하고, 등록·수정·삭제는 OWNER 권한을 가진 사용자만 접근하도록 구성 Soft Delete 메뉴 삭제는 실제 DB에서 데이터를 삭제하는 대신 deleted = true 로 변경하는 Soft Delete 방식 조회할 때는 삭제되지 않은 데이터만 조회 findByIdAndDeletedFalse() 주문이 기존 메뉴를 참조하고 있기 때문에 데이터를 실제로 삭제하지 않고 _상태만 변경_하는 방식 6. JPA 연관관계 메뉴와 사용자처럼 Entity 간 관계가 필요한 경우 JPA 연관관계 사용 이번 프로젝트에서는 주로 N:1 관계 를 사용 User 1 ↑ │ N Menu @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; 연관관계의 특징 DB에서는 외래 키를 통해 관계를 표현하고, JPA에서는 Entity 간 참조를 통해 관계를 표현 이번 프로젝트에서는 양방향 관계를 만들기보다 필요한 방향에서만 참조하는 단방향 연관관계 사용 7. 지연 로딩 @ManyToOne 의 기본 FetchType은 EAGER이지만 이번 프로젝트에서는 직접 LAZY 로 설정 @ManyToOne(fetch = FetchType.LAZY) LAZY 연관된 Entity를 바로 조회하지 않고 실제로 사용하는 시점에 조회 불필요한 데이터를 한 번에 조회하는 것을 줄일 수 있음 단, LAZY로 설정한 연관 Entity를 사용하려면 영속성 컨텍스트가 유지되는 상태 여야 함 따라서 Service의 @Transactional 과 함께 이해할 필요가 있음 8. 더티 체킹 JPA에서는 @Transactional 내부에서 _Entity의 값을 변경_하면 별도의 save() 없이도 변경 사항을 DB에 반영할 수 있음 @Transactional public void updateMenu(...) { menu.setName(name); } 트랜잭션이 종료될 때 JPA가 Entity의 변경 내용을 확인하고 UPDATE 쿼리를 실행 이를 더티 체킹(Dirty Checking) 이라고 함 단, readOnly = true 인 트랜잭션에서는 변경 사항이 정상적으로 반영되지 않음 9. 주문 주문에서는 주문 상태에 따라 기능을 제한 ORDERED ↓ ACCEPTED ↓ DELIVERED 각 상태에 맞지 않는 요청이 들어오면 409 Conflict 로 처리 중요한 부분 주문 금액은 Client가 전달한 값을 그대로 사용하지 않고 서버에서 직접 계산 Client의 금액 ↓ 신뢰하지 않음 메뉴 가격 × 수량 ↓ Server에서 계산 Client에서 임의로 금액을 변경하더라도 서버의 계산 결과를 사용하도록 구현 10. 주문 상태 변경과 @Transactional 주문 수락이나 배달 완료처럼 DB의 상태를 변경하는 메서드에는 @Transactional 필요 @Transactional public void acceptOrder(...) { order.setStatus(OrderStatus.ACCEPTED); } @Transactional(readOnly = true) 가 적용된 클래스에서 상태 변경 메서드에 별도의 @Transactional 이 없으면 변경 사항이 DB에 반영되지 않을 수 있음 정리 조회 → readOnly = true 데이터 변경 → @Transactional 11. 결제 결제 API에서는 주문의 총 금액을 기준으로 결제 처리 결제 수단은 Enum 으로 관리 public enum PaymentMethod { CARD } DTO에서 PaymentMethod 타입으로 받으면 잘못된 값이 들어왔을 때 Enum 변환 과정에서 400 Bad Request 발생 단, 값 자체가 없는 경우는 별도의 검증이 필요하기 때문에 @NotNull 사용 @NotNull private PaymentMethod method; 12. 결제 중복 처리 결제는 한 주문에 대해 한 번만 가능하도록 처리 ORDERED ↓ 결제 ↓ PAID 이미 결제가 완료된 주문에 다시 결제를 요청하면 409 Conflict 반환 상태 변경 조건을 Service에서 검사하여 비즈니스 규칙 적용 프로젝트를 진행하면서 겪었던 문제 1. @RequestBody 누락 Postman에서 JSON을 전달했는데 DTO의 값이 전부 null 로 들어오는 문제가 발생 원인은 @RequestBody 누락 @Valid @RequestBody SignupRequestDto requestDto JSON 데이터를 Controller에서 받기 위해 @RequestBody 필요 2. Validation 예외가 try-catch 에 잡히지 않는 문제 Controller 메서드에 진입하기 전에 @Valid 검증이 실행되기 때문에 Validation 예외는 메서드 내부의 try-catch 에서 처리할 수 없음 Request ↓ @Valid ↓ Validation ↓ Controller 따라서 @Valid 검증 실패는 일반적인 Controller 내부 예외 처리와 동작 시점이 다름 3. JWT 검증 실패가 200으로 반환되는 문제 JWT 검증 Filter에서 단순히 return; 으로 종료하면 응답을 작성하지 않은 상태로 요청이 끝나 200 OK가 반환되는 문제 가 발생 인증 필터에서 요청을 적절하게 다음 Filter Chain으로 넘겨 Spring Security가 권한 검사를 처리하도록 해야 함 4. PostgreSQL 예약어 문제 order , user 와 같은 이름은 DB에서 예약어로 사용될 수 있음 따라서 주문 테이블은 @Table(name = "p_order") 처럼 별도의 이름을 지정 핵심 개념 정리 개념 정리 3 Layer Controller → Service → Repository @RequestBody HTTP Body의 JSON을 객체로 변환 @Valid 요청 데이터 검증 BCrypt 비밀번호 암호화 Authentication 사용자 인증 Authorization 권한 확인 JWT Stateless 인증 방식 SecurityFilterChain Spring Security의 인증·인가 처리 STATELESS 세션을 사용하지 않는 방식 @ManyToOne N:1 연관관계 LAZY 필요한 시점에 연관 Entity 조회 Dirty Checking Entity 변경 사항 자동 반영 Soft Delete 실제 삭제 대신 삭제 상태 저장 @Transactional 트랜잭션 단위 처리 PUT 전체 수정 PATCH 일부 수정 409 Conflict 비즈니스 상태 충돌 EnumType.STRING Enum 값을 문자열로 저장 느낀 점 강의에서 배울 때는 각각의 개념을 이해했다고 생각했지만, 직접 프로젝트를 구현해보니 개념 하나하나가 실제 코드에서 어떻게 연결되는지 를 이해하는 것이 더 중요하다는 것을 느꼈다 특히 403 에러가 발생했을 때 단순히 권한 문제라고 생각했지만, 실제로는 CSRF 설정이나 /error 접근 제한 등 여러 원인이 있을 수 있었다 결국 에러가 발생했을 때 단순히 상태 코드만 확인하는 것이 아니라 요청이 어느 계층과 Filter에서 처리되고 있는지 추적하는 과정이 중요 하다는 것을 배웠다 로그를 통해 해결해야 하니까 어디서 오류가 났는지 파악하는 눈을 길러야겠다 또한 요구사항을 기능 단위로 나누고 회원가입 → 로그인 → 메뉴 → 주문 → 결제 순서로 구현하면서 앞에서 만든 기능이 다음 기능의 기반이 된다는 것도 체감했다 오늘은 단순히 코드를 작성하는 것보다 왜 이 설정이 필요한지, 이 코드가 어떤 조건을 해결하기 위해 존재하는지 를 생각하면서 개발하는 것이 왜 중요한지 체감했다
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
프로젝트 회고 및 개념 정리. 미니 배달 주문 서비스 프로젝트 회고 및 개념 정리 Spring Boot 4.1.1 · Java 21 · PostgreSQL · JWT 미니 배달 주문 서비스 회원가입부터 로그인, 메뉴, 주문, 결제까지 직접 구현하면서 Spring에서 배운 개념들을 실제 프로젝트에 적용해보는 과정 1. 프로젝트 구조 프로젝트는 도메인별로 user , menu , order , payment 를 나누고 각 도메인 내부에서 controller , service , repository , entity , dto 로 계층을 분리했다 3 Layer Architecture Controller ↓ Service ↓ Repository ↓ Database 각 계층의 역할을 분리하고 의존 방향을 한쪽으로…
Открыть источник