Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Sangwan/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
Expand All @@ -42,3 +43,15 @@ dependencies {
tasks.named('test') {
useJUnitPlatform()
}

tasks.named('bootRun') {
def envFile = file('.env')
if (envFile.exists()) {
envFile.readLines()
.findAll { line -> line.trim() && !line.trim().startsWith('#') && line.contains('=') }
.each { line ->
def (key, value) = line.split('=', 2)
environment key.trim(), value.trim()
}
}
}
6 changes: 4 additions & 2 deletions Sangwan/keyword_summary/ch07.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,8 @@

또한 `@Validated`는 **클래스 레벨**에 붙여 메서드 파라미터 검증도 가능하다.

단, 중첩 객체 필드는 `@Validated`만으로 자동 재귀 검증되지 않으며, 해당 필드에 `@Valid`를 함께 붙여야 검증된다.

```java
@Service
@Validated // 클래스 레벨에 붙이면 메서드 파라미터도 검증 가능
Expand All @@ -439,7 +441,7 @@
| 출처 | Jakarta Bean Validation (Java 표준) | Spring Framework |
| 패키지 | jakarta.validation.Valid | org.springframework.validation.annotation.Validated |
| 그룹 검증 | 지원 안 함 | 지원 (groups 속성 사용) |
| 중첩 객체 검증 | 지원 (@Valid 재귀 적용) | 지원 안 함 |
| 중첩 객체 검증 | 지원 (@Valid 재귀 적용) | @Valid를 함께 붙이면 가능 |
| 클래스 레벨 사용 | 불가 | 가능 (메서드 파라미터 검증) |
| 검증 실패 예외 | MethodArgumentNotValidException | ConstraintViolationException |

Expand Down Expand Up @@ -482,4 +484,4 @@
// @Validated 검증 실패 처리
}
}
```
```
348 changes: 348 additions & 0 deletions Sangwan/keyword_summary/ch08.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc10th.domain.inquire.controller;

public class InquireController {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc10th.domain.inquire.converter;

public class InquireConverter {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc10th.domain.inquire.dto;

public class InquireReqDTO {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc10th.domain.inquire.dto;

public class InquireResDTO {
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.example.umc10th.domain.qna.entity;
package com.example.umc10th.domain.inquire.entity;

import com.example.umc10th.domain.member.entity.Member;
import com.example.umc10th.domain.qna.enums.InquireStatus;
import com.example.umc10th.domain.qna.enums.InquireType;
import com.example.umc10th.domain.inquire.enums.InquireStatus;
import com.example.umc10th.domain.inquire.enums.InquireType;
import com.example.umc10th.global.entity.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.umc10th.domain.qna.entity;
package com.example.umc10th.domain.inquire.entity;

import com.example.umc10th.domain.member.entity.Member;
import com.example.umc10th.global.entity.BaseEntity;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.umc10th.domain.qna.entity;
package com.example.umc10th.domain.inquire.entity;

import com.example.umc10th.global.entity.BaseEntity;
import jakarta.persistence.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.umc10th.domain.qna.enums;
package com.example.umc10th.domain.inquire.enums;

public enum InquireStatus {
WAITING, COMPLETED
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.umc10th.domain.inquire.enums;

public enum InquireType {
SERVICE, STORE, PAYMENT, ACCOUNT, ETC
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.example.umc10th.domain.qna.exception;
package com.example.umc10th.domain.inquire.exception;

import com.example.umc10th.global.apiPayload.code.BaseErrorCode;
import com.example.umc10th.global.apiPayload.exception.ProjectException;

public class QnaException extends ProjectException {
public QnaException(BaseErrorCode errorCode) {
public class InquireException extends ProjectException {
public InquireException(BaseErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.umc10th.domain.qna.exception.code;
package com.example.umc10th.domain.inquire.exception.code;

import com.example.umc10th.global.apiPayload.code.BaseErrorCode;
import lombok.Getter;
Expand All @@ -7,9 +7,9 @@

@Getter
@RequiredArgsConstructor
public enum QnaErrorCode implements BaseErrorCode {
public enum InquireErrorCode implements BaseErrorCode {

QNA_NOT_FOUND(HttpStatus.NOT_FOUND, "QNA_404", "해당 문의를 찾을 수 없습니다.");
INQUIRE_NOT_FOUND(HttpStatus.NOT_FOUND, "INQUIRE_404", "해당 문의를 찾을 수 없습니다.");

private final HttpStatus status;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.umc10th.domain.qna.exception.code;
package com.example.umc10th.domain.inquire.exception.code;

import com.example.umc10th.global.apiPayload.code.BaseSuccessCode;
import lombok.Getter;
Expand All @@ -7,9 +7,9 @@

@Getter
@RequiredArgsConstructor
public enum QnaSuccessCode implements BaseSuccessCode {
public enum InquireSuccessCode implements BaseSuccessCode {

QNA_CREATED(HttpStatus.CREATED, "QNA_201", "문의 등록이 완료되었습니다.");
INQUIRE_CREATED(HttpStatus.CREATED, "INQUIRE_201", "문의 등록이 완료되었습니다.");

private final HttpStatus status;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.umc10th.domain.inquire.repository;

import com.example.umc10th.domain.inquire.entity.Inquire;
import org.springframework.data.jpa.repository.JpaRepository;

public interface InquireRepository extends JpaRepository<Inquire, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc10th.domain.inquire.service;

public class InquireService {
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.example.umc10th.global.dto.CursorPageRes;
import com.example.umc10th.global.dto.OffsetPageRes;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
Expand Down Expand Up @@ -42,7 +44,8 @@ public ApiResponse<CursorPageRes<MemberResDTO.MissionItem>> getMissions(
@RequestParam Long memberId, // TODO: 인증 구현 후 SecurityContext로 대체
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security 설정과 AuthMember가 추가되었으므로 /me 성격의 API는 요청 파라미터의 memberId보다 @AuthenticationPrincipal AuthMember에서 현재 로그인한 회원 ID를 가져오는 방향을 권장합니다. 이렇게 하면 클라이언트가 다른 회원 ID를 전달하는 흐름을 막고, 인증/인가 책임이 Controller 경계에서 더 명확해집니다.

@RequestParam String status,
@RequestParam(required = false) Long cursor,
@RequestParam(defaultValue = "10") int size
@RequestParam(defaultValue = "10")
@Positive(message = "페이지 크기는 1 이상이어야 합니다.") int size
) {
return ApiResponse.onSuccess(MemberSuccessCode.MISSION_LIST,
memberService.getMissions(memberId, status, cursor, size));
Expand All @@ -61,8 +64,10 @@ public ApiResponse<MemberResDTO.MissionSuccessRes> requestMissionSuccess(
@PostMapping("/me/missions/inprogress")
public ApiResponse<OffsetPageRes<MemberResDTO.MissionItem>> getInProgressMissions(
@Valid @RequestBody MemberReqDTO.GetInProgressMissionsReq request,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
@RequestParam(defaultValue = "0")
@PositiveOrZero(message = "페이지 번호는 0 이상이어야 합니다.") int page,
@RequestParam(defaultValue = "10")
@Positive(message = "페이지 크기는 1 이상이어야 합니다.") int size
) {
return ApiResponse.onSuccess(MemberSuccessCode.INPROGRESS_MISSIONS,
memberService.getInProgressMissions(request, page, size));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,68 @@
package com.example.umc10th.domain.member.converter;

import com.example.umc10th.domain.member.dto.MemberReqDTO;
import com.example.umc10th.domain.member.dto.MemberResDTO;
import com.example.umc10th.domain.member.entity.Member;
import com.example.umc10th.domain.member.entity.mapping.MemberAgreement;
import com.example.umc10th.domain.member.entity.mapping.MemberFoodCategory;
import com.example.umc10th.domain.member.entity.mapping.RegionProgress;
import com.example.umc10th.domain.member.enums.MemberStatus;
import com.example.umc10th.domain.member.enums.Role;
import com.example.umc10th.domain.mission.entity.Mission;
import com.example.umc10th.domain.mission.entity.mapping.MemberMission;
import com.example.umc10th.domain.store.entity.FoodCategory;
import com.example.umc10th.domain.term.entity.Term;
import com.example.umc10th.global.dto.CursorPageRes;
import com.example.umc10th.global.dto.OffsetPageRes;
import org.springframework.data.domain.Page;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;

public class MemberConverter {

public static Member toMember(MemberReqDTO.SignupReq request, String encodedPassword, LocalDate birth) {
return Member.builder()
.email(request.email())
.password(encodedPassword)
.name(request.name())
.role(Role.USER)
.status(MemberStatus.ACTIVE)
.step(0)
.totalPoint(0)
.gender(request.gender())
.birth(birth)
.baseAddress(request.address())
.isVerified(false)
.build();
}

public static MemberAgreement toMemberAgreement(Member member, Term term, Boolean isAgreed) {
return MemberAgreement.builder()
.member(member)
.term(term)
.isAgreed(isAgreed)
.build();
}

public static MemberFoodCategory toMemberFoodCategory(Member member, FoodCategory foodCategory) {
return MemberFoodCategory.builder()
.member(member)
.foodCategory(foodCategory)
.build();
}

public static MemberResDTO.SignupRes toSignupRes(Member member) {
return MemberResDTO.SignupRes.builder()
.memberId(member.getId())
.name(member.getName())
.createdAt(member.getCreatedAt())
.build();
}

public static MemberResDTO.MyInfoRes toGetInfo(Member member) {
return MemberResDTO.MyInfoRes.builder()
.email(member.getEmail())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package com.example.umc10th.domain.member.dto;

import com.example.umc10th.domain.member.enums.Gender;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.Size;

import java.util.List;

public class MemberReqDTO {

Expand All @@ -13,10 +20,23 @@ public record MyInfoReq(

public record SignupReq(
@NotBlank String name,
@NotBlank @Email String email,
@NotBlank
@Size(min = 8, max = 16, message = "비밀번호는 8자 이상 16자 이하여야 합니다.") String password,
@NotNull(message = "성별은 필수입니다. (MALE, FEMALE, NONE)") Gender gender,
@NotBlank(message = "생년월일은 필수입니다.")
@Pattern(regexp = "\\d{4}-\\d{2}-\\d{2}", message = "생년월일 형식이 올바르지 않습니다. (YYYY-MM-DD)") String birthday,
@NotBlank String address
@NotBlank String address,
@NotEmpty(message = "약관 동의 정보는 필수입니다.")
List<@Valid TermAgreementReq> termAgreements,
@NotEmpty(message = "선호 음식 종류는 1개 이상 선택해야 합니다.")
List<@NotNull @Positive Long> foodCategoryIds
) {}

public record TermAgreementReq(
@NotNull(message = "약관 ID는 필수입니다.")
@Positive(message = "약관 ID는 양수여야 합니다.") Long termId,
@NotNull(message = "약관 동의 여부는 필수입니다.") Boolean isAgreed
) {}

public record GetInProgressMissionsReq(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import com.example.umc10th.domain.notification.entity.NotificationSetting;
import com.example.umc10th.domain.point.entity.PointHistory;
import com.example.umc10th.domain.point.entity.PointWithdrawal;
import com.example.umc10th.domain.qna.entity.Inquire;
import com.example.umc10th.domain.inquire.entity.Inquire;
import com.example.umc10th.domain.review.entity.Review;
import com.example.umc10th.global.entity.BaseEntity;
import jakarta.persistence.*;
Expand All @@ -34,9 +34,12 @@ public class Member extends BaseEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
@Column(nullable = false, unique = true)
private String email;

@Column(nullable = false, length = 100)
private String password;

@Enumerated(EnumType.STRING)
private SocialType socialType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ public enum MemberErrorCode implements BaseErrorCode {

MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER_404", "해당 사용자를 찾을 수 없습니다."),
MEMBER_ALREADY_EXISTS(HttpStatus.CONFLICT, "MEMBER_4001", "이미 가입된 회원 정보가 존재합니다."),
INVALID_BIRTHDAY_FORMAT(HttpStatus.BAD_REQUEST, "MEMBER_4002", "생년월일 형식이 올바르지 않습니다. (YYYY-MM-DD)");
INVALID_BIRTHDAY_FORMAT(HttpStatus.BAD_REQUEST, "MEMBER_4002", "생년월일 형식이 올바르지 않습니다. (YYYY-MM-DD)"),
REQUIRED_TERM_NOT_AGREED(HttpStatus.BAD_REQUEST, "MEMBER_4003", "필수 약관에 동의해야 합니다."),
TERM_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER_4041", "해당 약관을 찾을 수 없습니다."),
FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER_4042", "해당 음식 카테고리를 찾을 수 없습니다.");

private final HttpStatus status;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.umc10th.domain.member.repository;

import com.example.umc10th.domain.member.entity.mapping.MemberAgreement;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberAgreementRepository extends JpaRepository<MemberAgreement, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.umc10th.domain.member.repository;

import com.example.umc10th.domain.member.entity.mapping.MemberFoodCategory;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberFoodCategoryRepository extends JpaRepository<MemberFoodCategory, Long> {
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.example.umc10th.domain.member.repository;

import com.example.umc10th.domain.member.dto.MemberReqDTO;
import com.example.umc10th.domain.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member,Long> {

boolean existsByEmail(String email);

Optional<Member> findByEmail(String email);
}
Loading