From ef592e7d4fef295606981a4b00d75f1126647232 Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Thu, 18 Jan 2024 14:28:36 +0900 Subject: [PATCH 01/11] =?UTF-8?q?[feat]=20Account=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=84=A4=EA=B3=84=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 공통 컬럼인 객체 생성일시와 출처를 JPA Auditing 기능을 활용하여 정보 저장 --- .../com/book/app/infra/config/JpaConfig.java | 10 ++++ .../com/book/app/modules/account/Account.java | 58 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/main/java/com/book/app/infra/config/JpaConfig.java create mode 100644 src/main/java/com/book/app/modules/account/Account.java diff --git a/src/main/java/com/book/app/infra/config/JpaConfig.java b/src/main/java/com/book/app/infra/config/JpaConfig.java new file mode 100644 index 0000000..e6999a0 --- /dev/null +++ b/src/main/java/com/book/app/infra/config/JpaConfig.java @@ -0,0 +1,10 @@ +package com.book.app.infra.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@EnableJpaAuditing +@Configuration +public class JpaConfig { + +} diff --git a/src/main/java/com/book/app/modules/account/Account.java b/src/main/java/com/book/app/modules/account/Account.java new file mode 100644 index 0000000..57b2e25 --- /dev/null +++ b/src/main/java/com/book/app/modules/account/Account.java @@ -0,0 +1,58 @@ +package com.book.app.modules.account; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.ToString; +import org.springframework.data.annotation.CreatedDate; + +import java.time.LocalDateTime; +import java.util.Objects; + +@Getter +@ToString +@Table(name = "ACCOUNT") +@Entity +public class Account { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ACCOUNT_ID") + private Long id; + + @Column(name = "EMAIL", unique = true, nullable = false, length = 100) + private String email; + + @Column(name = "NICKNAME", unique = true, nullable = false, length = 20) + private String nickname; + + @CreatedDate + @Column(name = "CREATED_DT", nullable = false) + private LocalDateTime createdAt; + + @Column(name = "PROFILE_IMG") + private String profileImg; + + protected Account() {} + + private Account(String email, String nickname) { + this.email = email; + this.nickname = nickname; + } + + public static Account of(String email, String nickname) { + return new Account(email, nickname); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Account account)) return false; + return id != null && id.equals(account.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} + From d2996893c6e78aec6e6546a137253dc662a1c2cd Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Mon, 22 Jan 2024 14:36:02 +0900 Subject: [PATCH 02/11] =?UTF-8?q?[update]=20Account=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=20=EC=86=8D=EC=84=B1=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/book/app/modules/account/Account.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/book/app/modules/account/Account.java b/src/main/java/com/book/app/modules/account/Account.java index 57b2e25..934995f 100644 --- a/src/main/java/com/book/app/modules/account/Account.java +++ b/src/main/java/com/book/app/modules/account/Account.java @@ -1,6 +1,10 @@ package com.book.app.modules.account; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Table; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; import lombok.Getter; import lombok.ToString; import org.springframework.data.annotation.CreatedDate; @@ -27,7 +31,7 @@ public class Account { @CreatedDate @Column(name = "CREATED_DT", nullable = false) - private LocalDateTime createdAt; + private LocalDateTime createdDt; @Column(name = "PROFILE_IMG") private String profileImg; @@ -55,4 +59,3 @@ public int hashCode() { return Objects.hash(id); } } - From d1b88fad6677a6cbbe11b4f91bac52925f20221a Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Mon, 22 Jan 2024 14:39:15 +0900 Subject: [PATCH 03/11] =?UTF-8?q?[update]=20Account=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EC=9D=98=20Annotation=20=EC=88=98=EC=A0=95=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/book/app/modules/account/Account.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/com/book/app/modules/account/Account.java b/src/main/java/com/book/app/modules/account/Account.java index 934995f..56414c4 100644 --- a/src/main/java/com/book/app/modules/account/Account.java +++ b/src/main/java/com/book/app/modules/account/Account.java @@ -1,10 +1,6 @@ package com.book.app.modules.account; -import jakarta.persistence.Column; -import jakarta.persistence.Table; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; +import jakarta.persistence.*; import lombok.Getter; import lombok.ToString; import org.springframework.data.annotation.CreatedDate; From 506f0c079663f04d6002d3eba8bba6de7d7b62b5 Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Thu, 25 Jan 2024 13:53:38 +0900 Subject: [PATCH 04/11] =?UTF-8?q?[update]=20Account=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=86=8D=EC=84=B1=20=EC=88=98=EC=A0=95=20-=20UID?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/book/app/modules/account/Account.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/book/app/modules/account/Account.java b/src/main/java/com/book/app/modules/account/Account.java index 56414c4..a0b9b39 100644 --- a/src/main/java/com/book/app/modules/account/Account.java +++ b/src/main/java/com/book/app/modules/account/Account.java @@ -1,46 +1,55 @@ package com.book.app.modules.account; import jakarta.persistence.*; +import lombok.AccessLevel; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.ToString; import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; import java.util.Objects; -@Getter @ToString +@Getter +@EntityListeners(AuditingEntityListener.class) +@NoArgsConstructor(access = AccessLevel.PROTECTED) @Table(name = "ACCOUNT") @Entity public class Account { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ACCOUNT_ID") + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "UID", unique = true, updatable = false, nullable = false) + private String uid; // Supabase 제공 + @Column(name = "EMAIL", unique = true, nullable = false, length = 100) private String email; @Column(name = "NICKNAME", unique = true, nullable = false, length = 20) private String nickname; + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) @CreatedDate - @Column(name = "CREATED_DT", nullable = false) - private LocalDateTime createdDt; + @Column(name = "JOIN_AT", nullable = false, updatable = false) + private LocalDateTime joinAt; @Column(name = "PROFILE_IMG") private String profileImg; - protected Account() {} - - private Account(String email, String nickname) { + private Account(String uid, String email, String nickname) { + this.uid = uid; this.email = email; this.nickname = nickname; } - public static Account of(String email, String nickname) { - return new Account(email, nickname); + public static Account of(String uid, String email, String nickname) { + return new Account(uid, email, nickname); } @Override @@ -55,3 +64,4 @@ public int hashCode() { return Objects.hash(id); } } + From 51f57b845b9efae84dfa1962730c84677c0cbb52 Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Thu, 25 Jan 2024 18:52:11 +0900 Subject: [PATCH 05/11] =?UTF-8?q?[env]=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC?= =?UTF-8?q?=EB=A6=AC=20validation=20=EC=B6=94=EA=B0=80=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 충돌 예상 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d211a42..f33899b 100644 --- a/build.gradle +++ b/build.gradle @@ -22,9 +22,9 @@ repositories { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' annotationProcessor 'org.projectlombok:lombok' compileOnly 'org.projectlombok:lombok' From 12055e5bb92799eb719c1f80d3b98c7af2ee2ad7 Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Thu, 25 Jan 2024 23:25:15 +0900 Subject: [PATCH 06/11] =?UTF-8?q?[feat]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5=20(=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85)=20API=20=EA=B5=AC=ED=98=84=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/book/app/modules/account/Account.java | 6 +++ .../modules/account/AccountController.java | 38 +++++++++++++++++ .../modules/account/AccountRepository.java | 11 +++++ .../modules/account/dto/AccountInfoDto.java | 41 +++++++++++++++++++ .../app/modules/account/dto/SignUpDto.java | 38 +++++++++++++++++ .../account/service/AccountService.java | 16 ++++++++ .../account/service/AccountServiceImpl.java | 28 +++++++++++++ .../global/response/SuccessResponseDto.java | 19 +++++++++ 8 files changed, 197 insertions(+) create mode 100644 src/main/java/com/book/app/modules/account/AccountController.java create mode 100644 src/main/java/com/book/app/modules/account/AccountRepository.java create mode 100644 src/main/java/com/book/app/modules/account/dto/AccountInfoDto.java create mode 100644 src/main/java/com/book/app/modules/account/dto/SignUpDto.java create mode 100644 src/main/java/com/book/app/modules/account/service/AccountService.java create mode 100644 src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java create mode 100644 src/main/java/com/book/app/modules/global/response/SuccessResponseDto.java diff --git a/src/main/java/com/book/app/modules/account/Account.java b/src/main/java/com/book/app/modules/account/Account.java index a0b9b39..d52a2d4 100644 --- a/src/main/java/com/book/app/modules/account/Account.java +++ b/src/main/java/com/book/app/modules/account/Account.java @@ -63,5 +63,11 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(id); } + + public String getProfileImg() { + return this.profileImg == null + ? "" + : this.profileImg; + } } diff --git a/src/main/java/com/book/app/modules/account/AccountController.java b/src/main/java/com/book/app/modules/account/AccountController.java new file mode 100644 index 0000000..86ae3f8 --- /dev/null +++ b/src/main/java/com/book/app/modules/account/AccountController.java @@ -0,0 +1,38 @@ +package com.book.app.modules.account; + +import com.book.app.modules.account.dto.AccountInfoDto; +import com.book.app.modules.account.dto.SignUpDto; +import com.book.app.modules.account.service.AccountService; +import com.book.app.modules.global.response.SuccessResponseDto; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@Validated +@RequiredArgsConstructor +@RequestMapping("/accounts") +@RestController +public class AccountController { + + private final AccountService accountService; + + // 사용자 정보 저장 + @PostMapping("/sign-up") + public ResponseEntity signUp(@RequestBody @Valid SignUpDto info) { + log.info("sign-up info = {}",info.toString()); + Account newAccount = accountService.saveSignUpInfo(info.toEntity()); + log.info("newAccount = {}", newAccount.toString()); + SuccessResponseDto result + = new SuccessResponseDto<>("account", AccountInfoDto.from(newAccount)); + return new ResponseEntity<>(result, HttpStatus.CREATED); + } + +} diff --git a/src/main/java/com/book/app/modules/account/AccountRepository.java b/src/main/java/com/book/app/modules/account/AccountRepository.java new file mode 100644 index 0000000..3e7f443 --- /dev/null +++ b/src/main/java/com/book/app/modules/account/AccountRepository.java @@ -0,0 +1,11 @@ +package com.book.app.modules.account; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface AccountRepository extends JpaRepository { + Optional findByNickname(String nickname); +} diff --git a/src/main/java/com/book/app/modules/account/dto/AccountInfoDto.java b/src/main/java/com/book/app/modules/account/dto/AccountInfoDto.java new file mode 100644 index 0000000..32084f4 --- /dev/null +++ b/src/main/java/com/book/app/modules/account/dto/AccountInfoDto.java @@ -0,0 +1,41 @@ +package com.book.app.modules.account.dto; + +import com.book.app.modules.account.Account; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 단일 사용자 데이터 + * @uid : Supabase에서 제공하는 사용자 식별자 + * @email : 로그인을 위한 이메일 + * @nickname : 사용자 별칭 + * @joinAt : DB 저장 날짜 및 시간 + * @profileImg : 프로필 이미지. null 이라면, length 0인 문자열 "" 반환 + */ +@Getter +@Builder +@AllArgsConstructor +public class AccountInfoDto { + + private String uid; + private String email; + private String nickname; + + @JsonProperty("join_at") + private LocalDateTime joinAt; + + @JsonProperty("profile_img") + private String profileImg; + + public static AccountInfoDto from(Account account) { + return AccountInfoDto.builder() + .uid(account.getUid()) + .email(account.getEmail()) + .nickname(account.getNickname()) + .joinAt(account.getJoinAt()) + .profileImg(account.getProfileImg()) + .build(); + } +} diff --git a/src/main/java/com/book/app/modules/account/dto/SignUpDto.java b/src/main/java/com/book/app/modules/account/dto/SignUpDto.java new file mode 100644 index 0000000..9c27189 --- /dev/null +++ b/src/main/java/com/book/app/modules/account/dto/SignUpDto.java @@ -0,0 +1,38 @@ +package com.book.app.modules.account.dto; + +import com.book.app.modules.account.Account; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.hibernate.validator.constraints.Length; + +/** + * 회원가입 데이터 + */ +@ToString +@Getter +public class SignUpDto { + + @NotBlank(message = "uid를 입력해주세요.") + private String uid; + + @Email(message = "이메일 형식에 맞춰주세요.") + @NotBlank(message = "이메일을 입력해주세요.") + private String email; + + /** + * 2~20자 길이의 한글, 영어(대소문자), 숫자(0-9) 만 허용 + */ + @NotBlank(message = "닉네임을 입력해주세요.") + @Length(min = 2, max = 20, message = "닉네임 길이는 2자 이상 20자 이하로 작성해주세요.") + @Pattern(regexp = "^[ㄱ-ㅎ가-힣A-Za-z0-9]{2,20}$", message = "닉네임은 한글, 영어(대소문자), 숫자만 허용하며 2자에서 20자 사이어야 합니다.") + private String nickname; + + public Account toEntity() { + return Account.of(this.uid, this.email, this.nickname); + } +} diff --git a/src/main/java/com/book/app/modules/account/service/AccountService.java b/src/main/java/com/book/app/modules/account/service/AccountService.java new file mode 100644 index 0000000..fdede0f --- /dev/null +++ b/src/main/java/com/book/app/modules/account/service/AccountService.java @@ -0,0 +1,16 @@ +package com.book.app.modules.account.service; + +import com.book.app.modules.account.Account; + +/** + * 사용자 CRUD에 해당하는 기능을 포함합니다. + */ +public interface AccountService { + + /** + * 회원가입을 하는 사용자의 정보(uid, email, nickname)를 DB에 저장합니다. + * 중복된 닉네임이 있는지 검증합니다. + */ + Account saveSignUpInfo(Account account); + +} diff --git a/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java b/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java new file mode 100644 index 0000000..6c84d18 --- /dev/null +++ b/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java @@ -0,0 +1,28 @@ +package com.book.app.modules.account.service; + +import com.book.app.modules.account.Account; +import com.book.app.modules.account.AccountRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Service +public class AccountServiceImpl implements AccountService { + + private final AccountRepository accountRepository; + + @Override + @Transactional + public Account saveSignUpInfo(Account account) { + verifyExistsNickname(account); + return accountRepository.save(account); + } + + private void verifyExistsNickname(Account account) { + accountRepository.findByNickname(account.getNickname()).ifPresent(e -> { + throw new IllegalArgumentException("중복된 닉네임입니다."); + }); + } +} diff --git a/src/main/java/com/book/app/modules/global/response/SuccessResponseDto.java b/src/main/java/com/book/app/modules/global/response/SuccessResponseDto.java new file mode 100644 index 0000000..e1b2906 --- /dev/null +++ b/src/main/java/com/book/app/modules/global/response/SuccessResponseDto.java @@ -0,0 +1,19 @@ +package com.book.app.modules.global.response; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class SuccessResponseDto { + + private String status; + private String domain; + private T data; + + public SuccessResponseDto(String domain, T data) { + this.status = "success"; + this.domain = domain; + this.data = data; + } +} From 75d3c6b733602c65e114adb56fd9357d6c349a2d Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Fri, 26 Jan 2024 15:05:57 +0900 Subject: [PATCH 07/11] =?UTF-8?q?[feat]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/account/AccountController.java | 20 +++++++++++++------ .../modules/account/AccountRepository.java | 1 + .../account/service/AccountService.java | 6 ++++++ .../account/service/AccountServiceImpl.java | 5 +++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/book/app/modules/account/AccountController.java b/src/main/java/com/book/app/modules/account/AccountController.java index 86ae3f8..d4d5495 100644 --- a/src/main/java/com/book/app/modules/account/AccountController.java +++ b/src/main/java/com/book/app/modules/account/AccountController.java @@ -10,10 +10,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @Slf4j @Validated @@ -27,12 +24,23 @@ public class AccountController { // 사용자 정보 저장 @PostMapping("/sign-up") public ResponseEntity signUp(@RequestBody @Valid SignUpDto info) { - log.info("sign-up info = {}",info.toString()); + log.info("sign-up info = {}",info); Account newAccount = accountService.saveSignUpInfo(info.toEntity()); - log.info("newAccount = {}", newAccount.toString()); + log.info("newAccount = {}", newAccount); SuccessResponseDto result = new SuccessResponseDto<>("account", AccountInfoDto.from(newAccount)); return new ResponseEntity<>(result, HttpStatus.CREATED); } + // 사용자 정보 조회 + @GetMapping("/info/{uid}") + public ResponseEntity getInfoAccount(@PathVariable("uid") String uid) { + log.info("uid = {}", uid); + Account getAccount = accountService.getAccountByUid(uid); + log.info("getAccount = {} ", getAccount); + SuccessResponseDto result + = new SuccessResponseDto<>("account", AccountInfoDto.from(getAccount)); + return new ResponseEntity<>(result, HttpStatus.OK); + } + } diff --git a/src/main/java/com/book/app/modules/account/AccountRepository.java b/src/main/java/com/book/app/modules/account/AccountRepository.java index 3e7f443..1ee4d22 100644 --- a/src/main/java/com/book/app/modules/account/AccountRepository.java +++ b/src/main/java/com/book/app/modules/account/AccountRepository.java @@ -8,4 +8,5 @@ @Repository public interface AccountRepository extends JpaRepository { Optional findByNickname(String nickname); + Optional findByUid(String uid); } diff --git a/src/main/java/com/book/app/modules/account/service/AccountService.java b/src/main/java/com/book/app/modules/account/service/AccountService.java index fdede0f..be26ac7 100644 --- a/src/main/java/com/book/app/modules/account/service/AccountService.java +++ b/src/main/java/com/book/app/modules/account/service/AccountService.java @@ -13,4 +13,10 @@ public interface AccountService { */ Account saveSignUpInfo(Account account); + /** + * uid에 해당하는 사용자 정보 조회 + * @param uid + * @return Account + */ + Account getAccountByUid(String uid); } diff --git a/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java b/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java index 6c84d18..295fa3e 100644 --- a/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java +++ b/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java @@ -20,6 +20,11 @@ public Account saveSignUpInfo(Account account) { return accountRepository.save(account); } + @Override + public Account getAccountByUid(String uid) { + return accountRepository.findByUid(uid).orElseThrow(() -> new NullPointerException("uid에 해당하는 사용자를 찾을 수 없습니다.")); + } + private void verifyExistsNickname(Account account) { accountRepository.findByNickname(account.getNickname()).ifPresent(e -> { throw new IllegalArgumentException("중복된 닉네임입니다."); From fc7824acd0cab3051a7dfa796a5779c6da9c14e7 Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Sat, 27 Jan 2024 18:10:57 +0900 Subject: [PATCH 08/11] =?UTF-8?q?[env]=20Json=20=EC=A7=81=EB=A0=AC?= =?UTF-8?q?=ED=99=94/=EC=97=AD=EC=A7=81=EB=A0=AC=ED=99=94=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index f33899b..bca0236 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'com.google.code.gson:gson' annotationProcessor 'org.projectlombok:lombok' compileOnly 'org.projectlombok:lombok' From 5d2f29abcd4421d25fb29899ebf01d32ea9bd04b Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Sat, 27 Jan 2024 18:12:24 +0900 Subject: [PATCH 09/11] =?UTF-8?q?[test]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/modules/account/dto/SignUpDto.java | 2 + .../account/service/AccountServiceImpl.java | 5 +- .../SebadogBookServerApplicationTests.java | 15 ---- .../java/com/book/app/infra/MockMvcTest.java | 20 +++++ .../account/AccountControllerTest.java | 87 +++++++++++++++++++ 5 files changed, 113 insertions(+), 16 deletions(-) delete mode 100644 src/test/java/com/book/app/SebadogBookServerApplicationTests.java create mode 100644 src/test/java/com/book/app/infra/MockMvcTest.java create mode 100644 src/test/java/com/book/app/modules/account/AccountControllerTest.java diff --git a/src/main/java/com/book/app/modules/account/dto/SignUpDto.java b/src/main/java/com/book/app/modules/account/dto/SignUpDto.java index 9c27189..715da61 100644 --- a/src/main/java/com/book/app/modules/account/dto/SignUpDto.java +++ b/src/main/java/com/book/app/modules/account/dto/SignUpDto.java @@ -15,6 +15,8 @@ */ @ToString @Getter +@AllArgsConstructor +@NoArgsConstructor public class SignUpDto { @NotBlank(message = "uid를 입력해주세요.") diff --git a/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java b/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java index 295fa3e..3b4f59e 100644 --- a/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java +++ b/src/main/java/com/book/app/modules/account/service/AccountServiceImpl.java @@ -3,9 +3,12 @@ import com.book.app.modules.account.Account; import com.book.app.modules.account.AccountRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.crossstore.ChangeSetPersister; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.NoSuchElementException; + @RequiredArgsConstructor @Transactional(readOnly = true) @Service @@ -22,7 +25,7 @@ public Account saveSignUpInfo(Account account) { @Override public Account getAccountByUid(String uid) { - return accountRepository.findByUid(uid).orElseThrow(() -> new NullPointerException("uid에 해당하는 사용자를 찾을 수 없습니다.")); + return accountRepository.findByUid(uid).orElseThrow(() -> new NoSuchElementException("uid에 해당하는 사용자를 찾을 수 없습니다.")); } private void verifyExistsNickname(Account account) { diff --git a/src/test/java/com/book/app/SebadogBookServerApplicationTests.java b/src/test/java/com/book/app/SebadogBookServerApplicationTests.java deleted file mode 100644 index 12ccf2f..0000000 --- a/src/test/java/com/book/app/SebadogBookServerApplicationTests.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.book.app; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Profile; - -@Profile("test") -@SpringBootTest -class SebadogAppTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/com/book/app/infra/MockMvcTest.java b/src/test/java/com/book/app/infra/MockMvcTest.java new file mode 100644 index 0000000..60b6927 --- /dev/null +++ b/src/test/java/com/book/app/infra/MockMvcTest.java @@ -0,0 +1,20 @@ +package com.book.app.infra; + +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@ActiveProfiles("test") +@Transactional +@SpringBootTest +@AutoConfigureMockMvc +public @interface MockMvcTest { +} diff --git a/src/test/java/com/book/app/modules/account/AccountControllerTest.java b/src/test/java/com/book/app/modules/account/AccountControllerTest.java new file mode 100644 index 0000000..b2d3f6c --- /dev/null +++ b/src/test/java/com/book/app/modules/account/AccountControllerTest.java @@ -0,0 +1,87 @@ +package com.book.app.modules.account; + +import com.book.app.infra.MockMvcTest; +import com.book.app.modules.account.dto.SignUpDto; +import com.book.app.modules.account.service.AccountService; +import com.google.gson.Gson; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@MockMvcTest +class AccountControllerTest { + + @Autowired MockMvc mockMvc; + @Autowired Gson gson; + + @Autowired AccountService accountService; + + final String url = "/accounts"; + + @DisplayName("회원가입 API 성공") + @Test + void signUp_success() throws Exception { + + SignUpDto request = new SignUpDto("test", "zzz@naver.com", "test"); + + mockMvc.perform(post(url+"/sign-up") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(gson.toJson(request))) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.status").value("success")) + .andExpect(jsonPath("$.domain").value("account")) + .andExpect(jsonPath("$.data.uid").value(request.getUid())) + .andExpect(jsonPath("$.data.email").value(request.getEmail())) + .andExpect(jsonPath("$.data.nickname").value(request.getNickname())); + } + + @Disabled + @DisplayName("회원가입 API 실패 - 중복 닉네임") + @Test + void signUp_fail_duplicated_nickname() throws Exception { + Account firstAccount = accountService.saveSignUpInfo(Account.of("first", "test@test.com", "test")); + + SignUpDto request = new SignUpDto("second", "test22@naver.com", firstAccount.getNickname()); + + mockMvc.perform(post(url+"/sign-up") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(gson.toJson(request))) + .andExpect(status().isInternalServerError()) + .andExpect(result -> assertTrue(result.getResolvedException() instanceof IllegalArgumentException)) + .andExpect(result -> assertEquals("중복된 닉네임입니다.", result.getResolvedException().getMessage())); + } + + @DisplayName("사용자 조회 API 성공") + @Test + void getInfoAccount_success() throws Exception { + Account expectAccount = accountService.saveSignUpInfo( + Account.of("first", "test@test.com", "test")); + + mockMvc.perform(get(url+"/info/{uid}",expectAccount.getUid())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value("success")) + .andExpect(jsonPath("$.domain").value("account")) + .andExpect(jsonPath("$.data.uid").value(expectAccount.getUid())) + .andExpect(jsonPath("$.data.email").value(expectAccount.getEmail())) + .andExpect(jsonPath("$.data.nickname").value(expectAccount.getNickname())); + } + + @Disabled + @DisplayName("사용자 조회 API 실패 - 존재하지 않는 UID") + @Test + void getInfoAccount_fail_notFoundAccount_byUid() throws Exception { + mockMvc.perform(get(url+"/info/{uid}","first")) + .andExpect(status().isNotFound()); + } +} \ No newline at end of file From f943fb18939d11e9bfd33dea5ea5f96c50bbfcd0 Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Sun, 28 Jan 2024 12:38:18 +0900 Subject: [PATCH 10/11] =?UTF-8?q?[update]=20dto=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/app/modules/account/dto/AccountInfoDto.java | 1 + .../com/book/app/modules/account/dto/SignUpDto.java | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/book/app/modules/account/dto/AccountInfoDto.java b/src/main/java/com/book/app/modules/account/dto/AccountInfoDto.java index 32084f4..95ead51 100644 --- a/src/main/java/com/book/app/modules/account/dto/AccountInfoDto.java +++ b/src/main/java/com/book/app/modules/account/dto/AccountInfoDto.java @@ -17,6 +17,7 @@ @Getter @Builder @AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class AccountInfoDto { private String uid; diff --git a/src/main/java/com/book/app/modules/account/dto/SignUpDto.java b/src/main/java/com/book/app/modules/account/dto/SignUpDto.java index 715da61..b87e0c3 100644 --- a/src/main/java/com/book/app/modules/account/dto/SignUpDto.java +++ b/src/main/java/com/book/app/modules/account/dto/SignUpDto.java @@ -4,19 +4,19 @@ import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import lombok.*; import org.hibernate.validator.constraints.Length; /** * 회원가입 데이터 + * @uid : Supabase에서 제공하는 사용자 식별자 + * @email : 로그인을 위한 이메일 + * @nickname : 사용자 별칭 */ @ToString @Getter @AllArgsConstructor -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class SignUpDto { @NotBlank(message = "uid를 입력해주세요.") From e52162e63f8abe859e64b09a63c249839039ac94 Mon Sep 17 00:00:00 2001 From: Si-Hyeak-KANG Date: Sun, 28 Jan 2024 16:38:16 +0900 Subject: [PATCH 11/11] =?UTF-8?q?[update]=20=EA=B3=B5=ED=86=B5=20url=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/modules/account/AccountControllerTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/book/app/modules/account/AccountControllerTest.java b/src/test/java/com/book/app/modules/account/AccountControllerTest.java index b2d3f6c..30b1d96 100644 --- a/src/test/java/com/book/app/modules/account/AccountControllerTest.java +++ b/src/test/java/com/book/app/modules/account/AccountControllerTest.java @@ -25,7 +25,7 @@ class AccountControllerTest { @Autowired AccountService accountService; - final String url = "/accounts"; + final String commonUrl = "/accounts"; @DisplayName("회원가입 API 성공") @Test @@ -33,7 +33,7 @@ void signUp_success() throws Exception { SignUpDto request = new SignUpDto("test", "zzz@naver.com", "test"); - mockMvc.perform(post(url+"/sign-up") + mockMvc.perform(post(commonUrl +"/sign-up") .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .content(gson.toJson(request))) @@ -53,7 +53,7 @@ void signUp_fail_duplicated_nickname() throws Exception { SignUpDto request = new SignUpDto("second", "test22@naver.com", firstAccount.getNickname()); - mockMvc.perform(post(url+"/sign-up") + mockMvc.perform(post(commonUrl +"/sign-up") .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .content(gson.toJson(request))) @@ -68,7 +68,7 @@ void getInfoAccount_success() throws Exception { Account expectAccount = accountService.saveSignUpInfo( Account.of("first", "test@test.com", "test")); - mockMvc.perform(get(url+"/info/{uid}",expectAccount.getUid())) + mockMvc.perform(get(commonUrl +"/info/{uid}",expectAccount.getUid())) .andExpect(status().isOk()) .andExpect(jsonPath("$.status").value("success")) .andExpect(jsonPath("$.domain").value("account")) @@ -81,7 +81,7 @@ void getInfoAccount_success() throws Exception { @DisplayName("사용자 조회 API 실패 - 존재하지 않는 UID") @Test void getInfoAccount_fail_notFoundAccount_byUid() throws Exception { - mockMvc.perform(get(url+"/info/{uid}","first")) + mockMvc.perform(get(commonUrl +"/info/{uid}","first")) .andExpect(status().isNotFound()); } } \ No newline at end of file