From a31f904f187cf0de8389f25c245f98173ab7e77b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:37:27 +0900 Subject: [PATCH 01/49] =?UTF-8?q?feat(application.yml):=20redis,=20securit?= =?UTF-8?q?y(oauth),=20frontend,=20logging=20level=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - redis host, port, password 정보 추가 - spring security, oauth(NAVER) 관련 정보 추가 - frontend root url 추가 (임시 - localhost) - security logging level DEBUG로 설정 --- src/main/resources/application.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a8c91bcd8..d6281a999 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,6 +15,29 @@ spring: generate_statistics: true show-sql: true + data: + redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT} + password: ${REDIS_PASSWORD} + + security: + oauth2: + client: + registration: + naver: + client-id: ${NAVER_CLIENT_ID} + client-secret: ${NAVER_CLIENT_SECRET} + redirect-uri: "http://localhost:8080/login/oauth2/code/naver" + authorization-grant-type: authorization_code + scope: ${NAVER_SCOPE} + + provider: + naver: + authorization-uri: https://nid.naver.com/oauth2.0/authorize + token-uri: https://nid.naver.com/oauth2.0/token + user-info-uri: https://openapi.naver.com/v1/nid/me + user-name-attribute: response # 네이버 API가 사용자 정보를 "response" 객체 안에 넣어 반환 #swagger springdoc: @@ -35,5 +58,12 @@ springdoc: paths-to-match: - /api/** +frontend: + url: localhost + jwt: secret: ${JWT_SECRET} + +logging: + level: + org.springframework.security: DEBUG \ No newline at end of file From cca332c150c7c658b1f2c1488cf250ac394c0121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:43:04 +0900 Subject: [PATCH 02/49] =?UTF-8?q?feat(RedisConfig):=20Redis=20Template,=20?= =?UTF-8?q?ConnectionFactory=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Redis 연결을 위한 LettuceConnectionFactory 설정 - RedisTemplate 설정 및 key/value 직렬화를 위한 StringRedisSerializer 적용 - RedisStandaloneConfiguration을 사용하여 host, port, password 설정 --- .../global/configure/RedisConfig.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/main/java/com/somemore/global/configure/RedisConfig.java diff --git a/src/main/java/com/somemore/global/configure/RedisConfig.java b/src/main/java/com/somemore/global/configure/RedisConfig.java new file mode 100644 index 000000000..a4fa30013 --- /dev/null +++ b/src/main/java/com/somemore/global/configure/RedisConfig.java @@ -0,0 +1,44 @@ +package com.somemore.global.configure; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +@EnableRedisRepositories +@RequiredArgsConstructor +public class RedisConfig { + + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Value("${spring.data.redis.password}") + private String password; + + @Bean + public LettuceConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port); + config.setPassword(password); + + return new LettuceConnectionFactory(config); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + return redisTemplate; + } +} From c0479f8aefd0b40f27151b5cfc7312e2f3057634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:44:04 +0900 Subject: [PATCH 03/49] =?UTF-8?q?feat(Tier):=20=EB=B4=89=EC=82=AC=EC=9E=90?= =?UTF-8?q?=20=EB=93=B1=EA=B8=89=20Enum=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RED부터 RAINBOW까지 8개의 등급 정의 --- .../com/somemore/volunteer/domain/Tier.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/domain/Tier.java diff --git a/src/main/java/com/somemore/volunteer/domain/Tier.java b/src/main/java/com/somemore/volunteer/domain/Tier.java new file mode 100644 index 000000000..b11256666 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/domain/Tier.java @@ -0,0 +1,22 @@ +package com.somemore.volunteer.domain; + +import lombok.Getter; + +@Getter +public enum Tier { + RED(1), + ORANGE(2), + YELLOW(3), + GREEN(4), + BLUE(5), + INDIGO(6), + VIOLET(7), + RAINBOW(8); + + private final int rank; + + Tier(int rank) { + this.rank = rank; + } + +} \ No newline at end of file From b77022c70e01a374fd2719de4c39d741b24e3ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:44:31 +0900 Subject: [PATCH 04/49] =?UTF-8?q?feat(UserRole):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EC=97=AD=ED=95=A0=20Enum=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VOLUNTEER, CENTER, ADMIN 3가지 사용자 역할 정의 - 각 역할은 시스템에서의 권한 구분을 위해 사용 --- src/main/java/com/somemore/auth/UserRole.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/somemore/auth/UserRole.java diff --git a/src/main/java/com/somemore/auth/UserRole.java b/src/main/java/com/somemore/auth/UserRole.java new file mode 100644 index 000000000..0e4d9ed8e --- /dev/null +++ b/src/main/java/com/somemore/auth/UserRole.java @@ -0,0 +1,7 @@ +package com.somemore.auth; + +public enum UserRole { + VOLUNTEER, + CENTER, + ADMIN +} From 82199e8ecab551e2dc601d4fd88ceb1963eac882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:47:26 +0900 Subject: [PATCH 05/49] =?UTF-8?q?feat(OAuthProvider):=20OAuth=20=EC=A0=9C?= =?UTF-8?q?=EA=B3=B5=EC=9E=90=20Enum=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NAVER 제공자를 위한 Enum 항목 추가 - providerName 필드를 통해 OAuth 제공자(registerId)의 이름 관리 - 문자열을 기반으로 OAuthProvider를 찾는 from 메서드 구현 --- .../somemore/auth/oauth/OAuthProvider.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/OAuthProvider.java diff --git a/src/main/java/com/somemore/auth/oauth/OAuthProvider.java b/src/main/java/com/somemore/auth/oauth/OAuthProvider.java new file mode 100644 index 000000000..c902aead5 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/OAuthProvider.java @@ -0,0 +1,24 @@ +package com.somemore.auth.oauth; + +import lombok.Getter; + +@Getter +public enum OAuthProvider { + NAVER("naver"); + + private final String providerName; + + OAuthProvider(String providerName) { + this.providerName = providerName; + } + + public static OAuthProvider from(String providerName) { + for (OAuthProvider provider : values()) { + if (provider.providerName.equals(providerName)) { + return provider; + } + } + + throw new IllegalArgumentException("올바르지 않은 OAuth 제공자: " + providerName); + } +} \ No newline at end of file From a3ef14231ce0a303b8847cc6576babd1ed147b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:05:25 +0900 Subject: [PATCH 06/49] =?UTF-8?q?feat(main):=20@EnableJpaAuditing=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BaseEntity 사용을 위해 @EnableJpaAuditing 어노테이션 추가 --- src/main/java/com/somemore/SomemoreApplication.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/somemore/SomemoreApplication.java b/src/main/java/com/somemore/SomemoreApplication.java index 87d5b8963..dec2413b5 100644 --- a/src/main/java/com/somemore/SomemoreApplication.java +++ b/src/main/java/com/somemore/SomemoreApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class SomemoreApplication { public static void main(String[] args) { From d4ee58c713ee2b38322d1abb55ffb98a9039b714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:26:06 +0900 Subject: [PATCH 07/49] =?UTF-8?q?feat(Tier):=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20rank=20=ED=95=84=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/volunteer/domain/Tier.java | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/somemore/volunteer/domain/Tier.java b/src/main/java/com/somemore/volunteer/domain/Tier.java index b11256666..7248fc805 100644 --- a/src/main/java/com/somemore/volunteer/domain/Tier.java +++ b/src/main/java/com/somemore/volunteer/domain/Tier.java @@ -4,19 +4,12 @@ @Getter public enum Tier { - RED(1), - ORANGE(2), - YELLOW(3), - GREEN(4), - BLUE(5), - INDIGO(6), - VIOLET(7), - RAINBOW(8); - - private final int rank; - - Tier(int rank) { - this.rank = rank; - } - + RED, + ORANGE, + YELLOW, + GREEN, + BLUE, + INDIGO, + VIOLET, + RAINBOW; } \ No newline at end of file From 0fd7a67e65cb7c53b310006c800b0f1caa2ccf73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:39:58 +0900 Subject: [PATCH 08/49] =?UTF-8?q?feat(Gender):=20=EC=84=B1=EB=B3=84=20ENUM?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20-=20=EB=82=A8,=20=EC=97=AC=20-=20?= =?UTF-8?q?=EC=9D=B4=EC=99=B8=EC=9D=98=20=EA=B0=92=EC=9D=80=20=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/volunteer/domain/Gender.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/domain/Gender.java diff --git a/src/main/java/com/somemore/volunteer/domain/Gender.java b/src/main/java/com/somemore/volunteer/domain/Gender.java new file mode 100644 index 000000000..064651d21 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/domain/Gender.java @@ -0,0 +1,25 @@ +package com.somemore.volunteer.domain; + +import lombok.Getter; + +@Getter +public enum Gender { + Male("M"), + Female("F"), + Undefined("U"); + + private final String code; + + Gender(String code) { + this.code = code; + } + + public static Gender from(String code) { + for (Gender gender : Gender.values()) { + if (gender.code.equalsIgnoreCase(code)) { + return gender; + } + } + return Undefined; + } +} \ No newline at end of file From 8416a64d966f6b0556244202bb069d9f787764fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:46:21 +0900 Subject: [PATCH 09/49] =?UTF-8?q?feat(dto):=20Volunteer=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=EC=9D=84=20=EC=9C=84=ED=95=9C=20VolunteerRegisterRequ?= =?UTF-8?q?estDto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/VolunteerRegisterRequestDto.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/dto/request/VolunteerRegisterRequestDto.java diff --git a/src/main/java/com/somemore/volunteer/dto/request/VolunteerRegisterRequestDto.java b/src/main/java/com/somemore/volunteer/dto/request/VolunteerRegisterRequestDto.java new file mode 100644 index 000000000..46625724d --- /dev/null +++ b/src/main/java/com/somemore/volunteer/dto/request/VolunteerRegisterRequestDto.java @@ -0,0 +1,15 @@ +package com.somemore.volunteer.dto.request; + +import com.somemore.auth.oauth.OAuthProvider; + +public record VolunteerRegisterRequestDto( + OAuthProvider oAuthProvider, + String oauthId, + String name, + String email, + String gender, + String birthday, + String birthyear, + String mobile +) { +} \ No newline at end of file From 5fa7a7488a3878ae9364a5e7c042e44f6047aa23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:48:13 +0900 Subject: [PATCH 10/49] =?UTF-8?q?feat(VolunteerDetail):=20VolunteerDetail?= =?UTF-8?q?=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 필드: - volunteerId: UUID (BINARY(16)) - gender: EnumType.STRING - birthDate: 문자열 (YYYY-MM-DD 형식) - contactNumber: 문자열 - 정적 팩토리 메서드를 통해 DTO로부터 VolunteerDetail 생성 가능 --- .../volunteer/domain/VolunteerDetail.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java diff --git a/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java b/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java new file mode 100644 index 000000000..0870a244c --- /dev/null +++ b/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java @@ -0,0 +1,70 @@ +package com.somemore.volunteer.domain; + +import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "volunteer_detail") +public class VolunteerDetail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "volunteer_id", nullable = false, columnDefinition = "BINARY(16)") + private UUID volunteerId; + + @Column(name = "name", nullable = false, length = 255) + private String name; + + @Column(name = "email", nullable = false, length = 255) + private String email; + + @Enumerated(EnumType.STRING) + @Column(name = "gender", nullable = false, length = 10) + private Gender gender; + + @Column(name = "birth_date", nullable = false, length = 10) + private String birthDate; + + @Column(name = "contact_number", nullable = false, length = 255) + private String contactNumber; + + + public static VolunteerDetail of(VolunteerRegisterRequestDto dto, UUID volunteerId) { + return VolunteerDetail.builder() + .volunteerId(volunteerId) + .name(dto.name()) + .email(dto.email()) + .gender(Gender.from(dto.gender())) + .birthDate(String.format("%s-%s", dto.birthyear(), dto.birthday())) + .contactNumber(dto.mobile()) + .build(); + } + + @Builder + private VolunteerDetail( + UUID volunteerId, + String name, + String email, + Gender gender, + String birthDate, + String contactNumber + ) { + this.volunteerId = volunteerId; + this.name = name; + this.email = email; + this.gender = gender; + this.birthDate = birthDate; + this.contactNumber = contactNumber; + } +} \ No newline at end of file From 945f6f9a010c82a630ab8f7efb4d200ac8e7e177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:49:37 +0900 Subject: [PATCH 11/49] =?UTF-8?q?feat(Volunteer):=20Volunteer=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BaseEntity 상속 - 필드: - id: UUID (BINARY(16), 고유 식별자) - oauthProvider: OAuth 인증 제공자 (EnumType.STRING) - oauthId: OAuth 제공자 ID - tier: 자원봉사 티어 (EnumType.STRING) - 정적 메서드를 통해 초기 값이 설정된 Volunteer 객체 생성 가능 --- .../somemore/volunteer/domain/Volunteer.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/domain/Volunteer.java diff --git a/src/main/java/com/somemore/volunteer/domain/Volunteer.java b/src/main/java/com/somemore/volunteer/domain/Volunteer.java new file mode 100644 index 000000000..bb7472a5e --- /dev/null +++ b/src/main/java/com/somemore/volunteer/domain/Volunteer.java @@ -0,0 +1,81 @@ +package com.somemore.volunteer.domain; + +import com.somemore.auth.oauth.OAuthProvider; +import com.somemore.global.common.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +import java.util.UUID; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "volunteer") +public class Volunteer extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(name = "id", nullable = false, updatable = false, columnDefinition = "BINARY(16)") + private UUID id; + + @Enumerated(EnumType.STRING) + @Column(name = "oauth_provider", nullable = false) + private OAuthProvider oauthProvider; + + @Column(name = "oauth_id", nullable = false) + private String oauthId; + + @Column(name = "nickname", nullable = false) + private String nickname; + + @Column(name = "img_url", nullable = false) + private String imgUrl; + + @Lob + @Column(name = "introduce", nullable = false) + private String introduce; + + @Enumerated(EnumType.STRING) + @Column(name = "tier", nullable = false, length = 20) + private Tier tier; + + @Column(name = "total_volunteer_hours", nullable = false) + private Integer totalVolunteerHours; + + @Column(name = "total_volunteer_count", nullable = false) + private Integer totalVolunteerCount; + + + public static Volunteer createDefault(OAuthProvider oauthProvider, String oauthId) { + return Volunteer.builder() + .oauthProvider(oauthProvider) + .oauthId(oauthId) + .nickname(UUID.randomUUID().toString().substring(0, 8)) + .imgUrl("") + .introduce("") + .tier(Tier.RED) + .totalVolunteerHours(0) + .totalVolunteerCount(0) + .build(); + } + + @Builder + private Volunteer( + OAuthProvider oauthProvider, + String oauthId, + String nickname, + String imgUrl, + String introduce, + Tier tier, + Integer totalVolunteerHours, + Integer totalVolunteerCount + ) { + this.oauthProvider = oauthProvider; + this.oauthId = oauthId; + this.nickname = nickname; + this.imgUrl = imgUrl; + this.introduce = introduce; + this.tier = tier; + this.totalVolunteerHours = totalVolunteerHours; + this.totalVolunteerCount = totalVolunteerCount; + } +} \ No newline at end of file From c3d65d8364d00bdd2ddd3bd28fc29e01bdc5a526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:50:30 +0900 Subject: [PATCH 12/49] =?UTF-8?q?feat(VolunteerRepository):=20VolunteerRep?= =?UTF-8?q?ository=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - findByOauthId: OAuth ID를 기준으로 Volunteer 조회 --- .../volunteer/repository/VolunteerRepository.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/repository/VolunteerRepository.java diff --git a/src/main/java/com/somemore/volunteer/repository/VolunteerRepository.java b/src/main/java/com/somemore/volunteer/repository/VolunteerRepository.java new file mode 100644 index 000000000..90140733f --- /dev/null +++ b/src/main/java/com/somemore/volunteer/repository/VolunteerRepository.java @@ -0,0 +1,14 @@ +package com.somemore.volunteer.repository; + +import com.somemore.volunteer.domain.Volunteer; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface VolunteerRepository extends JpaRepository { + + Optional findByOauthId(String OauthId); +} From a37bf73c0e703ab23187f67f749bb077980dfc7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:50:55 +0900 Subject: [PATCH 13/49] =?UTF-8?q?feat(VolunteerDetailRepository):=20Volunt?= =?UTF-8?q?eerDetailRepository=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../volunteer/repository/VolunteerDetailRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java diff --git a/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java b/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java new file mode 100644 index 000000000..9433b4c9e --- /dev/null +++ b/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java @@ -0,0 +1,7 @@ +package com.somemore.volunteer.repository; + +import com.somemore.volunteer.domain.VolunteerDetail; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface VolunteerDetailRepository extends JpaRepository { +} From 345e610225d1b8aabb5b603c11e719db559600bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:53:05 +0900 Subject: [PATCH 14/49] =?UTF-8?q?feat(cookie):=20=EC=BF=A0=ED=82=A4=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=EC=9D=84=20=EC=9C=84=ED=95=9C=20SetCookieUse?= =?UTF-8?q?Case=20=EB=B0=8F=20SetCookieService=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SetCookieService 클래스 구현: - SetCookieUseCase를 구현한 쿠키 설정 서비스 - setToken 메서드를 통해 토큰 타입(TokenType)에 따라 쿠키 생성 및 응답 헤더에 추가 - generateCookie 메서드로 HttpOnly, Secure, SameSite 등 안전한 쿠키 생성 로직 포함 - ResponseCookie를 활용 --- .../auth/cookie/SetCookieService.java | 30 +++++++++++++++++++ .../auth/cookie/SetCookieUseCase.java | 8 +++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/com/somemore/auth/cookie/SetCookieService.java create mode 100644 src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java diff --git a/src/main/java/com/somemore/auth/cookie/SetCookieService.java b/src/main/java/com/somemore/auth/cookie/SetCookieService.java new file mode 100644 index 000000000..f728dac31 --- /dev/null +++ b/src/main/java/com/somemore/auth/cookie/SetCookieService.java @@ -0,0 +1,30 @@ +package com.somemore.auth.cookie; + +import com.somemore.auth.jwt.domain.TokenType; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseCookie; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class SetCookieService implements SetCookieUseCase { + + @Override + public void setToken(HttpServletResponse response, String value, TokenType tokenType) { + ResponseCookie cookie = generateCookie(tokenType.name(), value, tokenType.getPeriodInSeconds()); + response.addHeader("Set-Cookie", cookie.toString()); + } + + private static ResponseCookie generateCookie(String name, String value, int time) { + return ResponseCookie.from(name, value) + .httpOnly(true) + .secure(true) + .path("/") + .maxAge(time) + .sameSite("Lax") + .build(); + } +} diff --git a/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java b/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java new file mode 100644 index 000000000..148e2e013 --- /dev/null +++ b/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java @@ -0,0 +1,8 @@ +package com.somemore.auth.cookie; + +import com.somemore.auth.jwt.domain.TokenType; +import jakarta.servlet.http.HttpServletResponse; + +public interface SetCookieUseCase { + void setToken(HttpServletResponse response, String value, TokenType tokenType); +} From 7d269627fa50f28269fa639f60264ece943cd485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:54:44 +0900 Subject: [PATCH 15/49] =?UTF-8?q?feat(volunteer):=20=EC=9E=90=EC=9B=90?= =?UTF-8?q?=EB=B4=89=EC=82=AC=EC=9E=90=20=EB=93=B1=EB=A1=9D=20UseCase=20?= =?UTF-8?q?=EB=B0=8F=20Service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RegisterVolunteerUseCase 구현체로 자원봉사자(유저) 등록 로직 처리 - Volunteer 엔티티 생성 및 VolunteerRepository에 저장 - VolunteerDetail 엔티티 생성 및 VolunteerDetailRepository에 저장 - createDefault 및 VolunteerDetail.of 메서드를 활용한 객체 생성 --- .../command/RegisterVolunteerService.java | 33 +++++++++++++++++++ .../command/RegisterVolunteerUseCase.java | 7 ++++ 2 files changed, 40 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java create mode 100644 src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java diff --git a/src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java b/src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java new file mode 100644 index 000000000..8ea14dea0 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java @@ -0,0 +1,33 @@ +package com.somemore.volunteer.service.command; + +import com.somemore.volunteer.domain.Volunteer; +import com.somemore.volunteer.domain.VolunteerDetail; +import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; +import com.somemore.volunteer.repository.VolunteerDetailRepository; +import com.somemore.volunteer.repository.VolunteerRepository; +import com.somemore.volunteer.usecase.command.RegisterVolunteerUseCase; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static com.somemore.volunteer.domain.Volunteer.createDefault; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional +public class RegisterVolunteerService implements RegisterVolunteerUseCase { + + private final VolunteerRepository volunteerRepository; + private final VolunteerDetailRepository volunteerDetailRepository; + + @Override + public void registerVolunteer(VolunteerRegisterRequestDto dto) { + Volunteer volunteer = createDefault(dto.oAuthProvider(), dto.oauthId()); + volunteerRepository.save(volunteer); + + VolunteerDetail volunteerDetail = VolunteerDetail.of(dto, volunteer.getId()); + volunteerDetailRepository.save(volunteerDetail); + } +} diff --git a/src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java b/src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java new file mode 100644 index 000000000..45e06dcf4 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java @@ -0,0 +1,7 @@ +package com.somemore.volunteer.usecase.command; + +import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; + +public interface RegisterVolunteerUseCase { + void registerVolunteer(VolunteerRegisterRequestDto dto); +} From f594fe7201295d58b709dd51fc81f2edbc7ab6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:57:09 +0900 Subject: [PATCH 16/49] =?UTF-8?q?feat(dto):=20NaverUserProfileResponseDto?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 네이버 사용자 프로필 정보를 처리하기 위한 DTO 클래스 추가 - 필드: - resultcode: 결과 코드 - message: 결과 메시지 - response: 응답 데이터 (중첩 record) - id: 네이버 사용자 일련 번호 - name: 이름 - email: 이메일 - gender: 성별 (F, M, U) - birthday: 생일 (MM-DD) - birthyear: 출생 연도 - mobile: 휴대 전화 번호 - JSON 직렬화를 위한 @JsonNaming - mobile-164 pattern 무시를 위한 @JsonIgnoreProperties - toVolunteerRegisterRequestDto 메서드 추가: - Naver 사용자 데이터를 VolunteerRegisterRequestDto로 변환 - OAuthProvider를 NAVER로 설정하여 Volunteer 등록 로직과 연동 --- .../response/NaverUserProfileResponseDto.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/naver/dto/response/NaverUserProfileResponseDto.java diff --git a/src/main/java/com/somemore/auth/oauth/naver/dto/response/NaverUserProfileResponseDto.java b/src/main/java/com/somemore/auth/oauth/naver/dto/response/NaverUserProfileResponseDto.java new file mode 100644 index 000000000..cf512d301 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/dto/response/NaverUserProfileResponseDto.java @@ -0,0 +1,38 @@ +package com.somemore.auth.oauth.naver.dto.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.somemore.auth.oauth.OAuthProvider; +import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public record NaverUserProfileResponseDto( + String resultcode, // 결과 코드 + String message, // 결과 메시지 + Response response // 응답 데이터 +) { + @JsonIgnoreProperties(ignoreUnknown = true) + public record Response( + String id, // 일련 번호 + String name, // 이름 + String email, // 이메일 + String gender, // 성별 (F, M, U) + String birthday, // 생일 (MM-DD) + String birthyear, // 출생 연도 + String mobile // 휴대 전화 번호 + ) {} + + public VolunteerRegisterRequestDto toVolunteerRegisterRequestDto() { + return new VolunteerRegisterRequestDto( + OAuthProvider.NAVER, + this.response.id(), + this.response.name(), + this.response.email(), + this.response.gender(), + this.response.birthday(), + this.response.birthyear(), + this.response.mobile() + ); + } +} \ No newline at end of file From 565e8942f7f30c4be7062faa5cd2106d1f89bf53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:58:07 +0900 Subject: [PATCH 17/49] =?UTF-8?q?feat(NaverUser):=20NaverUser=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 네이버 사용자 정보를 저장하기 위한 NaverUser 엔티티 구현 - 정적 팩토리 메서드: OAuth ID를 기반으로 NaverUser 객체를 생성 --- .../auth/oauth/naver/domain/NaverUser.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/naver/domain/NaverUser.java diff --git a/src/main/java/com/somemore/auth/oauth/naver/domain/NaverUser.java b/src/main/java/com/somemore/auth/oauth/naver/domain/NaverUser.java new file mode 100644 index 000000000..c99b56519 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/domain/NaverUser.java @@ -0,0 +1,23 @@ +package com.somemore.auth.oauth.naver.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.*; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "naver_user") +public class NaverUser { + @Id + private String oauthId; + + private NaverUser(String oauthId) { + this.oauthId = oauthId; + } + + public static NaverUser from(String oauthId) { + return new NaverUser(oauthId); + } +} From b9d7d6d52a0114e597dd995f6cdc4508799ac394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:58:33 +0900 Subject: [PATCH 18/49] =?UTF-8?q?feat(NaverUserRepository):=20NaverUserRep?= =?UTF-8?q?ository=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/oauth/naver/repository/NaverUserRepository.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/naver/repository/NaverUserRepository.java diff --git a/src/main/java/com/somemore/auth/oauth/naver/repository/NaverUserRepository.java b/src/main/java/com/somemore/auth/oauth/naver/repository/NaverUserRepository.java new file mode 100644 index 000000000..5d0128d7f --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/repository/NaverUserRepository.java @@ -0,0 +1,9 @@ +package com.somemore.auth.oauth.naver.repository; + +import com.somemore.auth.oauth.naver.domain.NaverUser; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface NaverUserRepository extends JpaRepository { +} From bf7ea95e31a1369c2748ce2d8b7c22ff0096d9ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:00:05 +0900 Subject: [PATCH 19/49] =?UTF-8?q?feat(converter):=20OAuth2User=EB=A5=BC=20?= =?UTF-8?q?NaverUserProfileResponseDto=EB=A1=9C=20=EB=B3=80=ED=99=98?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EC=9C=A0=ED=8B=B8=EB=A6=AC=ED=8B=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OAuth2User 객체의 attributes를 NaverUserProfileResponseDto로 변환 - ObjectMapper를 사용하여 JSON 데이터를 DTO로 매핑 --- .../oauth/naver/util/OAuthResponseConverter.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java diff --git a/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java b/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java new file mode 100644 index 000000000..180508305 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java @@ -0,0 +1,16 @@ +package com.somemore.auth.oauth.naver.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.somemore.auth.oauth.naver.dto.response.NaverUserProfileResponseDto; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import java.util.Map; + +public class OAuthResponseConverter { + public static NaverUserProfileResponseDto convertToNaverUserProfileResponseDto(OAuth2User oAuth2User) { + ObjectMapper objectMapper = new ObjectMapper(); + + Map attributes = oAuth2User.getAttributes(); + return objectMapper.convertValue(attributes, NaverUserProfileResponseDto.class); + } +} From f6216895e99cd5fea1abfc4c4f55aea0a2984cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:02:26 +0900 Subject: [PATCH 20/49] =?UTF-8?q?feat(redirect):=20=EB=A6=AC=EB=8B=A4?= =?UTF-8?q?=EC=9D=B4=EB=A0=89=ED=8A=B8=20=EC=B2=98=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20config=20=EB=B0=8F=20UsaCase,=20Service=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RedirectConfig: - RedirectStrategy Bean: - DefaultRedirectStrategy를 사용하여 리다이렉트 처리 - RedirectService: - RedirectStrategy를 사용하여 주어진 URL로 클라이언트를 리다이렉트 --- .../auth/redirect/RedirectConfig.java | 15 ++++++++++++ .../auth/redirect/RedirectService.java | 23 +++++++++++++++++++ .../auth/redirect/RedirectUseCase.java | 9 ++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/main/java/com/somemore/auth/redirect/RedirectConfig.java create mode 100644 src/main/java/com/somemore/auth/redirect/RedirectService.java create mode 100644 src/main/java/com/somemore/auth/redirect/RedirectUseCase.java diff --git a/src/main/java/com/somemore/auth/redirect/RedirectConfig.java b/src/main/java/com/somemore/auth/redirect/RedirectConfig.java new file mode 100644 index 000000000..6a1aeb791 --- /dev/null +++ b/src/main/java/com/somemore/auth/redirect/RedirectConfig.java @@ -0,0 +1,15 @@ +package com.somemore.auth.redirect; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.web.DefaultRedirectStrategy; +import org.springframework.security.web.RedirectStrategy; + +@Configuration +public class RedirectConfig { + + @Bean + public RedirectStrategy redirectStrategy() { + return new DefaultRedirectStrategy(); + } +} diff --git a/src/main/java/com/somemore/auth/redirect/RedirectService.java b/src/main/java/com/somemore/auth/redirect/RedirectService.java new file mode 100644 index 000000000..aa2c25a4c --- /dev/null +++ b/src/main/java/com/somemore/auth/redirect/RedirectService.java @@ -0,0 +1,23 @@ +package com.somemore.auth.redirect; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.web.RedirectStrategy; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +@RequiredArgsConstructor +@Slf4j +public class RedirectService implements RedirectUseCase { + + private final RedirectStrategy redirectStrategy; + + @Override + public void redirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException { + redirectStrategy.sendRedirect(request, response, url); + } +} diff --git a/src/main/java/com/somemore/auth/redirect/RedirectUseCase.java b/src/main/java/com/somemore/auth/redirect/RedirectUseCase.java new file mode 100644 index 000000000..9f9e86153 --- /dev/null +++ b/src/main/java/com/somemore/auth/redirect/RedirectUseCase.java @@ -0,0 +1,9 @@ +package com.somemore.auth.redirect; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +public interface RedirectUseCase { + void redirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException; +} \ No newline at end of file From ed054e0c80950369b697831cec3c3c66af1215be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:35:18 +0900 Subject: [PATCH 21/49] =?UTF-8?q?feat(CheckNaverUser):=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B2=84=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A1=B4?= =?UTF-8?q?=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20UseCase=20=EB=B0=8F=20Service=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/query/CheckNaverUserService.java | 22 +++++++++++++++++++ .../usecase/query/CheckNaverUserUseCase.java | 5 +++++ 2 files changed, 27 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/naver/service/query/CheckNaverUserService.java create mode 100644 src/main/java/com/somemore/auth/oauth/naver/usecase/query/CheckNaverUserUseCase.java diff --git a/src/main/java/com/somemore/auth/oauth/naver/service/query/CheckNaverUserService.java b/src/main/java/com/somemore/auth/oauth/naver/service/query/CheckNaverUserService.java new file mode 100644 index 000000000..58e1e99a5 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/service/query/CheckNaverUserService.java @@ -0,0 +1,22 @@ +package com.somemore.auth.oauth.naver.service.query; + +import com.somemore.auth.oauth.naver.repository.NaverUserRepository; +import com.somemore.auth.oauth.naver.usecase.query.CheckNaverUserUseCase; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class CheckNaverUserService implements CheckNaverUserUseCase { + + private final NaverUserRepository naverUserRepository; + + @Override + public boolean isNaverUserExists(String id) { + return naverUserRepository.existsById(id); + } +} diff --git a/src/main/java/com/somemore/auth/oauth/naver/usecase/query/CheckNaverUserUseCase.java b/src/main/java/com/somemore/auth/oauth/naver/usecase/query/CheckNaverUserUseCase.java new file mode 100644 index 000000000..d9cf1e6c2 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/usecase/query/CheckNaverUserUseCase.java @@ -0,0 +1,5 @@ +package com.somemore.auth.oauth.naver.usecase.query; + +public interface CheckNaverUserUseCase { + boolean isNaverUserExists(String id); +} From afa6d2cc0434812413339a359bdcc0b7a9f92309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:37:03 +0900 Subject: [PATCH 22/49] =?UTF-8?q?feat(ProcessOAuthUser):=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B2=84=20OAuth=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20UseCase=20=EB=B0=8F=20Service=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/ProcessNaverOAuthUserService.java | 43 +++++++++++++++++++ .../usecase/ProcessOAuthUserUseCase.java | 10 +++++ 2 files changed, 53 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/naver/service/query/ProcessNaverOAuthUserService.java create mode 100644 src/main/java/com/somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java diff --git a/src/main/java/com/somemore/auth/oauth/naver/service/query/ProcessNaverOAuthUserService.java b/src/main/java/com/somemore/auth/oauth/naver/service/query/ProcessNaverOAuthUserService.java new file mode 100644 index 000000000..df5c864ce --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/service/query/ProcessNaverOAuthUserService.java @@ -0,0 +1,43 @@ +package com.somemore.auth.oauth.naver.service.query; + +import com.somemore.auth.oauth.OAuthProvider; +import com.somemore.auth.oauth.naver.usecase.query.CheckNaverUserUseCase; +import com.somemore.auth.oauth.usecase.ProcessOAuthUserUseCase; +import com.somemore.auth.oauth.naver.dto.response.NaverUserProfileResponseDto; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import static com.somemore.auth.oauth.naver.util.OAuthResponseConverter.convertToNaverUserProfileResponseDto; + +@Slf4j +@Component +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ProcessNaverOAuthUserService implements ProcessOAuthUserUseCase { + + private final CheckNaverUserUseCase checkNaverUserUseCase; + + @Override + public String processOAuthUser(Authentication authentication) { + OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); + return processUserInformation(oAuth2User); + } + + private String processUserInformation(OAuth2User oAuth2User) { + NaverUserProfileResponseDto dto = convertToNaverUserProfileResponseDto(oAuth2User); + String oAuthId = dto.response().id(); + + if (checkNaverUserUseCase.isNaverUserExists(oAuthId)) { + return oAuthId; + } + + log.error("유저가 회원 가입을 진행했으나, 존재하지 않는 상태입니다. OAuth Provider: {}, OAuth ID: {}", + OAuthProvider.NAVER, + oAuthId); + throw new IllegalStateException(); + } +} diff --git a/src/main/java/com/somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java b/src/main/java/com/somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java new file mode 100644 index 000000000..acc1fcaa7 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java @@ -0,0 +1,10 @@ +package com.somemore.auth.oauth.usecase; + +import jakarta.servlet.ServletException; +import org.springframework.security.core.Authentication; + +import java.io.IOException; + +public interface ProcessOAuthUserUseCase { + String processOAuthUser(Authentication authentication) throws IOException, ServletException; +} \ No newline at end of file From 70a483330860320ee1872f60a6e868c63d7bec0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:39:39 +0900 Subject: [PATCH 23/49] =?UTF-8?q?feat(RegisterNaverUser):=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B2=84=20OAuth=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20UsaCase,=20Service=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../command/RegisterNaverUserService.java | 25 +++++++++++++++++++ .../command/RegisterNaverUserUseCase.java | 5 ++++ 2 files changed, 30 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/naver/service/command/RegisterNaverUserService.java create mode 100644 src/main/java/com/somemore/auth/oauth/naver/usecase/command/RegisterNaverUserUseCase.java diff --git a/src/main/java/com/somemore/auth/oauth/naver/service/command/RegisterNaverUserService.java b/src/main/java/com/somemore/auth/oauth/naver/service/command/RegisterNaverUserService.java new file mode 100644 index 000000000..29ac3b291 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/service/command/RegisterNaverUserService.java @@ -0,0 +1,25 @@ +package com.somemore.auth.oauth.naver.service.command; + +import com.somemore.auth.oauth.naver.domain.NaverUser; +import com.somemore.auth.oauth.naver.repository.NaverUserRepository; +import com.somemore.auth.oauth.naver.usecase.command.RegisterNaverUserUseCase; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional +public class RegisterNaverUserService implements RegisterNaverUserUseCase { + + private final NaverUserRepository naverUserRepository; + + @Override + public void registerNaverUser(String oAuthId) { + NaverUser naverUser = NaverUser.from(oAuthId); + + naverUserRepository.save(naverUser); + } +} diff --git a/src/main/java/com/somemore/auth/oauth/naver/usecase/command/RegisterNaverUserUseCase.java b/src/main/java/com/somemore/auth/oauth/naver/usecase/command/RegisterNaverUserUseCase.java new file mode 100644 index 000000000..b87a0e797 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/usecase/command/RegisterNaverUserUseCase.java @@ -0,0 +1,5 @@ +package com.somemore.auth.oauth.naver.usecase.command; + +public interface RegisterNaverUserUseCase { + void registerNaverUser(String oAuthId); +} From 29a4695d09ceb4c6b21e8cbd8aba7acb2b949761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:42:21 +0900 Subject: [PATCH 24/49] =?UTF-8?q?feat(auth):=20OAuth=20=EC=8B=A4=ED=8C=A8?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=ED=95=B8=EB=93=A4=EB=9F=AC(CustomOAuth?= =?UTF-8?q?FailureHandler)=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SimpleUrlAuthenticationFailureHandler를 상속하여 OAuth 인증 실패 처리, 로그 기록 - 프론트엔드와의 협의 후 추가 처리 로직 구현 예정 (TODO 추가) --- .../failure/CustomOAuthFailureHandler.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/handler/failure/CustomOAuthFailureHandler.java diff --git a/src/main/java/com/somemore/auth/oauth/handler/failure/CustomOAuthFailureHandler.java b/src/main/java/com/somemore/auth/oauth/handler/failure/CustomOAuthFailureHandler.java new file mode 100644 index 000000000..d7ba81196 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/handler/failure/CustomOAuthFailureHandler.java @@ -0,0 +1,21 @@ +package com.somemore.auth.oauth.handler.failure; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CustomOAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler { + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) { + // TODO 프론트엔드와 협의 + log.error("안녕 난 말하는 감자야"); + } +} From bec05720e1bac1891ca08998904168509bbb4bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:45:04 +0900 Subject: [PATCH 25/49] =?UTF-8?q?feat(NaverOAuth):=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20OAuth2=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자 정보 처리 로직: - OAuth2User를 NaverUserProfileResponseDto로 변환 - 사용자 존재 여부 확인 (CheckNaverUserUseCase) - 신규 사용자일 경우: - 네이버 사용자 등록 (RegisterNaverUserUseCase) - 자원봉사자 등록 (RegisterVolunteerUseCase) - 기존 사용자일 경우 OAuth2User 반환 --- .../command/NaverOAuth2UserInfoService.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java diff --git a/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java b/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java new file mode 100644 index 000000000..496dcf1f3 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java @@ -0,0 +1,43 @@ +package com.somemore.auth.oauth.naver.service.command; + +import com.somemore.auth.oauth.naver.dto.response.NaverUserProfileResponseDto; +import com.somemore.auth.oauth.naver.usecase.query.CheckNaverUserUseCase; +import com.somemore.auth.oauth.naver.usecase.command.RegisterNaverUserUseCase; +import com.somemore.auth.oauth.naver.util.OAuthResponseConverter; +import com.somemore.volunteer.usecase.command.RegisterVolunteerUseCase; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@RequiredArgsConstructor +@Service +@Transactional +public class NaverOAuth2UserInfoService { + + private final CheckNaverUserUseCase checkNaverUserUseCase; + private final RegisterNaverUserUseCase registerNaverUserUseCase; + private final RegisterVolunteerUseCase registerVolunteerUseCase; + + public OAuth2User processOAuth2User(OAuth2User oAuth2User) { + NaverUserProfileResponseDto dto = OAuthResponseConverter.convertToNaverUserProfileResponseDto(oAuth2User); + String naverOauthId = dto.response().id(); + + if (isNewUser(naverOauthId)) { + registerUser(dto); + } + + return oAuth2User; + } + + private boolean isNewUser(String id) { + return !checkNaverUserUseCase.isNaverUserExists(id); + } + + private void registerUser(NaverUserProfileResponseDto dto) { + registerNaverUserUseCase.registerNaverUser(dto.response().id()); + registerVolunteerUseCase.registerVolunteer(dto.toVolunteerRegisterRequestDto()); + } +} From af51871bedf2cceea02ba9d8210b8bf53c2a9971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:51:19 +0900 Subject: [PATCH 26/49] =?UTF-8?q?feat(OAuthUser):=20CustomOAuth2UserServic?= =?UTF-8?q?e=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20OAuth=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DefaultOAuth2UserService를 활용하여 OAuth2 사용자 로드 - OAuthProvider에 따라 네이버 사용자 처리 로직 분기: - NaverOAuth2UserInfoService와 연동 - 추후에 추가 가능 --- .../service/CustomOAuth2UserService.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/service/CustomOAuth2UserService.java diff --git a/src/main/java/com/somemore/auth/oauth/service/CustomOAuth2UserService.java b/src/main/java/com/somemore/auth/oauth/service/CustomOAuth2UserService.java new file mode 100644 index 000000000..087fa0c44 --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/service/CustomOAuth2UserService.java @@ -0,0 +1,39 @@ +package com.somemore.auth.oauth.service; + +import com.somemore.auth.oauth.OAuthProvider; +import com.somemore.auth.oauth.naver.service.command.NaverOAuth2UserInfoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class CustomOAuth2UserService implements OAuth2UserService { + + private final NaverOAuth2UserInfoService naverOAuth2UserInfoService; + + @Override + public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { + try { + OAuth2UserService oAuth2UserService = new DefaultOAuth2UserService(); + OAuth2User oAuth2User = oAuth2UserService.loadUser(userRequest); + + return switch (OAuthProvider.from(getRegistrationId(userRequest))) { + case NAVER -> naverOAuth2UserInfoService.processOAuth2User(oAuth2User); + }; + } catch (OAuth2AuthenticationException e) { + log.error("OAuth 사용자 정보를 로드하는 중 문제가 발생했습니다: {}", e.getMessage(), e); + throw e; + } + } + + private static String getRegistrationId(OAuth2UserRequest userRequest) { + return userRequest.getClientRegistration().getRegistrationId(); + } +} \ No newline at end of file From 8e4da9b3fa52b6ba107edf4d5c43fb5a17a4f779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:52:50 +0900 Subject: [PATCH 27/49] =?UTF-8?q?feat(FindVolunteer):=20OAuth=20ID?= =?UTF-8?q?=EB=A1=9C=20Volunteer=20ID=20=EC=A1=B0=ED=9A=8C=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/query/FindVolunteerIdService.java | 27 +++++++++++++++++++ .../usecase/query/FindVolunteerIdUseCase.java | 7 +++++ 2 files changed, 34 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java create mode 100644 src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java diff --git a/src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java b/src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java new file mode 100644 index 000000000..b6c7e6ee3 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java @@ -0,0 +1,27 @@ +package com.somemore.volunteer.service.query; + +import com.somemore.volunteer.repository.VolunteerRepository; +import com.somemore.volunteer.usecase.query.FindVolunteerIdUseCase; +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.UUID; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FindVolunteerIdService implements FindVolunteerIdUseCase { + + private final VolunteerRepository volunteerRepository; + + @Override + public UUID findVolunteerIdByOAuthId(String oAuthId) { + return volunteerRepository.findByOauthId(oAuthId) + .orElseThrow(EntityNotFoundException::new) + .getId(); + } +} diff --git a/src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java b/src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java new file mode 100644 index 000000000..6427cb5db --- /dev/null +++ b/src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java @@ -0,0 +1,7 @@ +package com.somemore.volunteer.usecase.query; + +import java.util.UUID; + +public interface FindVolunteerIdUseCase { + UUID findVolunteerIdByOAuthId(String oAuthId); +} From ace54e174480d95acffa8e9c7cfa2ae643ea00c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 23:06:01 +0900 Subject: [PATCH 28/49] =?UTF-8?q?feat(OAuthSuccessHandler):=20OAuth=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=84=B1=EA=B3=B5=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=ED=95=B8=EB=93=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - onAuthenticationSuccess 구현: - OAuthProvider에 따라 사용자 정보를 처리 (현재는 NAVER만 지원) - getOAuthProvider: 인증된 OAuth 제공자 추출 - 지원하지 않는 OAuth 제공자에 대한 예외 처리 추가 - ProcessNaverOAuthUserService를 통해 네이버 사용자 정보 처리 - FindVolunteerIdUseCase로 OAuth ID 기반 Volunteer ID 조회 - GenerateTokensOnLoginUseCase로 RefreshToken 저장 및 AccessToken 생성 - SetCookieUseCase를 사용하여 AccessToken을 클라이언트 쿠키에 저장 - RedirectUseCase로 프론트엔드 URL로 리다이렉션 수행 --- .../success/CustomOAuthSuccessHandler.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java diff --git a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java new file mode 100644 index 000000000..5f389367b --- /dev/null +++ b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java @@ -0,0 +1,62 @@ +package com.somemore.auth.oauth.handler.success; + +import com.somemore.auth.cookie.SetCookieUseCase; +import com.somemore.auth.jwt.domain.EncodedToken; +import com.somemore.auth.jwt.domain.TokenType; +import com.somemore.auth.jwt.usecase.command.GenerateTokensOnLoginUseCase; +import com.somemore.auth.oauth.OAuthProvider; +import com.somemore.auth.oauth.naver.service.query.ProcessNaverOAuthUserService; +import com.somemore.auth.redirect.RedirectUseCase; +import com.somemore.volunteer.usecase.query.FindVolunteerIdUseCase; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +@Slf4j +public class CustomOAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + + private final ProcessNaverOAuthUserService processNaverOAuthService; + private final FindVolunteerIdUseCase findVolunteerIdUseCase; + private final GenerateTokensOnLoginUseCase generateTokensOnLoginUseCase; + private final SetCookieUseCase setCookieUseCase; + private final RedirectUseCase redirectUseCase; + + @Value("${frontend.url}") + private String frontendRootUrl; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { + String oAuthId; + switch (getOAuthProvider(authentication)) { + case NAVER -> oAuthId = processNaverOAuthService.processOAuthUser(authentication); + default -> { + log.error("지원하지 않는 OAuth 제공자입니다."); + throw new IllegalArgumentException(); + } + } + + UUID volunteerId = findVolunteerIdUseCase.findVolunteerIdByOAuthId(oAuthId); + EncodedToken accessToken = generateTokensOnLoginUseCase.saveRefreshTokenAndReturnAccessToken(volunteerId); + + setCookieUseCase.setToken(response, accessToken.value(), TokenType.ACCESS); + redirectUseCase.redirect(request, response, frontendRootUrl); + } + + private static OAuthProvider getOAuthProvider(Authentication authentication) { + if (authentication instanceof OAuth2AuthenticationToken token) { + return OAuthProvider.from(token.getAuthorizedClientRegistrationId()); + } + throw new IllegalArgumentException(); + } +} From 8d05dc3be1c51949ff6a811d89e01caec8ba67e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 23:07:21 +0900 Subject: [PATCH 29/49] =?UTF-8?q?chore:=20Volunteer=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=82=AD=EC=A0=9C=20(=EB=8B=A4=EB=A5=B8=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EC=97=90=20=EC=9E=88=EC=9D=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/somemore/domains/Volunteer.java | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 src/main/java/com/somemore/domains/Volunteer.java diff --git a/src/main/java/com/somemore/domains/Volunteer.java b/src/main/java/com/somemore/domains/Volunteer.java deleted file mode 100644 index 9377ebcc1..000000000 --- a/src/main/java/com/somemore/domains/Volunteer.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.somemore.domains; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Lob; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Entity -public class Volunteer { - @Id - @Column(name = "id", nullable = false, length = 16) - private String id; - - @Column(name = "oauth_provider", nullable = false) - private String oauthProvider; - - @Column(name = "oauth_id", nullable = false) - private String oauthId; - - @Column(name = "name", nullable = false) - private String name; - - @Column(name = "nickname", nullable = false) - private String nickname; - - @Column(name = "img_url", nullable = false) - private String imgUrl; - - @Lob - @Column(name = "introduce", nullable = false) - private String introduce; - - @Column(name = "tier", nullable = false, length = 20) - private String tier; - - @Column(name = "total_volunteer_hours", nullable = false) - private Integer totalVolunteerHours; - - @Column(name = "total_volunteer_count", nullable = false) - private Integer totalVolunteerCount; - -} \ No newline at end of file From 2e53b62711e6bda3e80ae944632af076e226972a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 23:10:57 +0900 Subject: [PATCH 30/49] =?UTF-8?q?feat(config):=20Spring=20Security=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20OAuth2=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=B2=98=EB=A6=AC=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Stateless 세션 관리 설정(SessionCreationPolicy.STATELESS) - CSRF, HTTP Basic, Form Login, Logout 기능 비활성화 - SecurityFilterChain 설정: - 공개 API 경로 설정 - 그 외 모든 요청 인증 필요 - OAuth2 인증 처리: - CustomOAuth2UserService로 사용자 정보 처리 - CustomOAuthSuccessHandler로 인증 성공 처리 - CustomOAuthFailureHandler로 인증 실패 처리 - TODO: - JWT 인증 필터 추가 - JWT 예외 필터 추가 --- .../global/configure/SecurityConfig.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/main/java/com/somemore/global/configure/SecurityConfig.java b/src/main/java/com/somemore/global/configure/SecurityConfig.java index c897da912..9f3560c0e 100644 --- a/src/main/java/com/somemore/global/configure/SecurityConfig.java +++ b/src/main/java/com/somemore/global/configure/SecurityConfig.java @@ -1,4 +1,67 @@ package com.somemore.global.configure; +import com.somemore.auth.oauth.handler.failure.CustomOAuthFailureHandler; +import com.somemore.auth.oauth.handler.success.CustomOAuthSuccessHandler; +import com.somemore.auth.oauth.service.CustomOAuth2UserService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; + +@RequiredArgsConstructor +@Configuration +@EnableWebSecurity +@EnableMethodSecurity +@Slf4j public class SecurityConfig { + + private final CustomOAuth2UserService customOAuth2UserService; + private final CustomOAuthSuccessHandler customOAuthSuccessHandler; + private final CustomOAuthFailureHandler customOAuthFailureHandler; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { + return httpSecurity + .csrf(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .logout(AbstractHttpConfigurer::disable) + .sessionManagement(sessionManagement -> + sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + + .authorizeHttpRequests(request -> + request + .requestMatchers( + "/login", + "/oauth2/**", + "/api/auth/**", + "/v3/api-docs/**", + "/swagger/**", + "/swagger-ui.html", + "/swagger-ui/**") + .permitAll() + .anyRequest().authenticated() + ) + + .oauth2Login(oauth2 -> + oauth2 + .userInfoEndpoint(userInfoEndpointConfig -> + userInfoEndpointConfig.userService(customOAuth2UserService)) + .failureHandler(customOAuthFailureHandler) + .successHandler(customOAuthSuccessHandler) + ).build(); + + +// // TODO JWT 인증 필터가 인증 요청 처리, JWT 인증 필터를 UsernamePasswordAuthenticationFilter 앞에 추가 +// return httpSecurity +// .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) +// .addFilterBefore(jwtExceptionFilter, JwtAuthFilter.class) +// .build(); + } } From d66389a4f0564297fd9ee56bcaf68374d4212cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sat, 23 Nov 2024 23:18:43 +0900 Subject: [PATCH 31/49] =?UTF-8?q?style:=20unused=20imports=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0,=20formatting=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/somemore/global/configure/SecurityConfig.java | 2 +- src/main/java/com/somemore/volunteer/domain/Tier.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/somemore/global/configure/SecurityConfig.java b/src/main/java/com/somemore/global/configure/SecurityConfig.java index 9f3560c0e..a044d7300 100644 --- a/src/main/java/com/somemore/global/configure/SecurityConfig.java +++ b/src/main/java/com/somemore/global/configure/SecurityConfig.java @@ -58,7 +58,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws ).build(); -// // TODO JWT 인증 필터가 인증 요청 처리, JWT 인증 필터를 UsernamePasswordAuthenticationFilter 앞에 추가 +// TODO JWT 인증 필터가 인증 요청 처리, JWT 인증 필터를 UsernamePasswordAuthenticationFilter 앞에 추가 // return httpSecurity // .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) // .addFilterBefore(jwtExceptionFilter, JwtAuthFilter.class) diff --git a/src/main/java/com/somemore/volunteer/domain/Tier.java b/src/main/java/com/somemore/volunteer/domain/Tier.java index 7248fc805..391efe625 100644 --- a/src/main/java/com/somemore/volunteer/domain/Tier.java +++ b/src/main/java/com/somemore/volunteer/domain/Tier.java @@ -11,5 +11,5 @@ public enum Tier { BLUE, INDIGO, VIOLET, - RAINBOW; + RAINBOW } \ No newline at end of file From 85b26754396ac1ba545d87c85f11dda8c80e575d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sun, 24 Nov 2024 17:42:07 +0900 Subject: [PATCH 32/49] =?UTF-8?q?refactor(VolunteerDetail):=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20length=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/somemore/volunteer/domain/VolunteerDetail.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java b/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java index 0870a244c..b55850387 100644 --- a/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java +++ b/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java @@ -23,10 +23,10 @@ public class VolunteerDetail { @Column(name = "volunteer_id", nullable = false, columnDefinition = "BINARY(16)") private UUID volunteerId; - @Column(name = "name", nullable = false, length = 255) + @Column(name = "name", nullable = false) private String name; - @Column(name = "email", nullable = false, length = 255) + @Column(name = "email", nullable = false) private String email; @Enumerated(EnumType.STRING) @@ -36,7 +36,7 @@ public class VolunteerDetail { @Column(name = "birth_date", nullable = false, length = 10) private String birthDate; - @Column(name = "contact_number", nullable = false, length = 255) + @Column(name = "contact_number", nullable = false) private String contactNumber; From 0c6e7f03c0837dea43664d976fa7367954d3cf9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:54:58 +0900 Subject: [PATCH 33/49] =?UTF-8?q?feat(config):=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=99=98=EA=B2=BD=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?Redis,=20=ED=94=84=EB=A1=A0=ED=8A=B8=EC=97=94=EB=93=9C,=20JWT?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트 환경에서 로컬 Redis(호스트: localhost, 포트: 6379) 설정 추가 - 테스트용 프론트엔드 URL(http://localhost:3000) 설정 추가 - JWT 토큰 생성을 위한 별도의 시크릿 키 설정 추가 --- src/test/resources/application-test.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 5e687f767..55537ee8e 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -19,4 +19,16 @@ spring: sql: init: - mode: never \ No newline at end of file + mode: never + + data: + redis: + host: localhost # 로컬 Redis 사용 + port: 6379 + password: # 테스트에서는 비밀번호 없이 연결 + +frontend: + url: http://localhost:3000 # 테스트용 프론트엔드 주소 + +jwt: + secret: 63bf2c80266cd25072e53b3482e318c30d1cd18d8c98d0f5d278530a94fe28d9fbbec531e5ccb58c725c125738182357786b71f43a7172c5d0c94a17f0da44f2 # 테스트용 JWT 시크릿 키 \ No newline at end of file From 35789a5a4496f3b97c9364b8d3670e6074213386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:55:51 +0900 Subject: [PATCH 34/49] =?UTF-8?q?feat(Gender):=20enum=20=EA=B0=92=EB=93=A4?= =?UTF-8?q?=EC=9D=84=20=EB=8C=80=EB=AC=B8=EC=9E=90=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/somemore/volunteer/domain/Gender.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/somemore/volunteer/domain/Gender.java b/src/main/java/com/somemore/volunteer/domain/Gender.java index 064651d21..c7786939b 100644 --- a/src/main/java/com/somemore/volunteer/domain/Gender.java +++ b/src/main/java/com/somemore/volunteer/domain/Gender.java @@ -4,9 +4,9 @@ @Getter public enum Gender { - Male("M"), - Female("F"), - Undefined("U"); + MALE("M"), + FEMALE("F"), + UNDEFINED("U"); private final String code; @@ -20,6 +20,6 @@ public static Gender from(String code) { return gender; } } - return Undefined; + return UNDEFINED; } } \ No newline at end of file From 189c7c91487d955db7e11433ba83eb5dd2a031c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:57:02 +0900 Subject: [PATCH 35/49] =?UTF-8?q?fix(IntegrationTestSupport):=20@JpaAuditi?= =?UTF-8?q?ng=20=EC=84=A4=EC=A0=95=EC=9D=84=20prod=20=ED=99=98=EA=B2=BD?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트 환경과 프로덕션 환경에서 중복된 @JpaAuditing 설정으로 발생한 문제 해결 - @JpaAuditing 활성화를 프로덕션 환경으로 제한하여 충돌 방지 --- src/test/java/com/somemore/IntegrationTestSupport.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/com/somemore/IntegrationTestSupport.java b/src/test/java/com/somemore/IntegrationTestSupport.java index 3a6c6934d..cba699570 100644 --- a/src/test/java/com/somemore/IntegrationTestSupport.java +++ b/src/test/java/com/somemore/IntegrationTestSupport.java @@ -1,11 +1,9 @@ package com.somemore; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.test.context.ActiveProfiles; @ActiveProfiles("test") -@EnableJpaAuditing @SpringBootTest public abstract class IntegrationTestSupport { From 1b032152464fad6a2110e8a0087215d5752846bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:58:08 +0900 Subject: [PATCH 36/49] =?UTF-8?q?feat(VolunteerDetail):=20VolunteerId?= =?UTF-8?q?=EB=A1=9C=20Detail=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../volunteer/repository/VolunteerDetailRepository.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java b/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java index 9433b4c9e..82e8f96f7 100644 --- a/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java +++ b/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java @@ -3,5 +3,9 @@ import com.somemore.volunteer.domain.VolunteerDetail; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; +import java.util.UUID; + public interface VolunteerDetailRepository extends JpaRepository { + Optional findByVolunteerId(UUID volunteerId); } From 41921e0870dccae84350ebef19fed2a792f03118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:09:12 +0900 Subject: [PATCH 37/49] =?UTF-8?q?style(=EA=B0=9C=ED=96=89):=20git=20?= =?UTF-8?q?=EC=9E=A0=EC=9E=AC=EC=A0=81=EC=9D=B8=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=98=88=EB=B0=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/somemore/volunteer/domain/Gender.java | 2 +- src/main/java/com/somemore/volunteer/domain/Tier.java | 2 +- src/main/java/com/somemore/volunteer/domain/Volunteer.java | 2 +- .../java/com/somemore/volunteer/domain/VolunteerDetail.java | 2 +- .../volunteer/dto/request/VolunteerRegisterRequestDto.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/somemore/volunteer/domain/Gender.java b/src/main/java/com/somemore/volunteer/domain/Gender.java index c7786939b..aaadb3a39 100644 --- a/src/main/java/com/somemore/volunteer/domain/Gender.java +++ b/src/main/java/com/somemore/volunteer/domain/Gender.java @@ -22,4 +22,4 @@ public static Gender from(String code) { } return UNDEFINED; } -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/volunteer/domain/Tier.java b/src/main/java/com/somemore/volunteer/domain/Tier.java index 391efe625..9a41b5111 100644 --- a/src/main/java/com/somemore/volunteer/domain/Tier.java +++ b/src/main/java/com/somemore/volunteer/domain/Tier.java @@ -12,4 +12,4 @@ public enum Tier { INDIGO, VIOLET, RAINBOW -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/volunteer/domain/Volunteer.java b/src/main/java/com/somemore/volunteer/domain/Volunteer.java index bb7472a5e..4a8d78b9d 100644 --- a/src/main/java/com/somemore/volunteer/domain/Volunteer.java +++ b/src/main/java/com/somemore/volunteer/domain/Volunteer.java @@ -78,4 +78,4 @@ private Volunteer( this.totalVolunteerHours = totalVolunteerHours; this.totalVolunteerCount = totalVolunteerCount; } -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java b/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java index b55850387..9f9321a29 100644 --- a/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java +++ b/src/main/java/com/somemore/volunteer/domain/VolunteerDetail.java @@ -67,4 +67,4 @@ private VolunteerDetail( this.birthDate = birthDate; this.contactNumber = contactNumber; } -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/volunteer/dto/request/VolunteerRegisterRequestDto.java b/src/main/java/com/somemore/volunteer/dto/request/VolunteerRegisterRequestDto.java index 46625724d..67dcf7a96 100644 --- a/src/main/java/com/somemore/volunteer/dto/request/VolunteerRegisterRequestDto.java +++ b/src/main/java/com/somemore/volunteer/dto/request/VolunteerRegisterRequestDto.java @@ -12,4 +12,4 @@ public record VolunteerRegisterRequestDto( String birthyear, String mobile ) { -} \ No newline at end of file +} From d0d31e2682498a625f7d0ca87cd307ba68b20437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:12:16 +0900 Subject: [PATCH 38/49] =?UTF-8?q?test(FindVolunteerIdService):=20=EB=B4=89?= =?UTF-8?q?=EC=82=AC=EC=9E=90=20ID=20=EC=A1=B0=ED=9A=8C=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 존재하는 OAuth ID로 봉사자 ID를 조회하는 성공 케이스 테스트 - 존재하지 않는 OAuth ID로 조회 시 예외를 던지는 실패 케이스 테스트 --- .../query/FindVolunteerIdServiceTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java diff --git a/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java b/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java new file mode 100644 index 000000000..88b03e787 --- /dev/null +++ b/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java @@ -0,0 +1,62 @@ +package com.somemore.volunteer.service.query; + +import com.somemore.IntegrationTestSupport; +import com.somemore.auth.oauth.OAuthProvider; +import com.somemore.volunteer.domain.Volunteer; +import com.somemore.volunteer.repository.VolunteerRepository; +import jakarta.persistence.EntityNotFoundException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@SpringBootTest +class FindVolunteerIdServiceTest extends IntegrationTestSupport { + + @Autowired + private FindVolunteerIdService findVolunteerIdService; + + @Autowired + private VolunteerRepository volunteerRepository; + + @AfterEach + void tearDown() { + volunteerRepository.deleteAllInBatch(); + } + + @DisplayName("존재하는 OAuth ID로 봉사자 ID를 조회한다") + @Test + void findVolunteerId() { + // given + String oAuthId = "example-oauth-id"; + Volunteer volunteer = Volunteer.createDefault(OAuthProvider.NAVER, oAuthId); + + volunteerRepository.save(volunteer); + + // when + UUID actualId = findVolunteerIdService.findVolunteerIdByOAuthId(oAuthId); + + // then + assertThat(actualId) + .isNotNull() + .isEqualTo(volunteer.getId()); + } + + @DisplayName("존재하지 않는 OAuth ID로 조회 시 예외를 던진다") + @Test + void throwExceptionWhenVolunteerNotFound() { + // given + String oAuthId = "non-existing-oauth-id"; + + // when + // then + assertThatThrownBy(() -> findVolunteerIdService.findVolunteerIdByOAuthId(oAuthId)) + .isInstanceOf(EntityNotFoundException.class); + } +} From b3fa19f32b823947627ac3ca0c644863f4a4490f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:12:40 +0900 Subject: [PATCH 39/49] =?UTF-8?q?test(RegisterVolunteerService):=20?= =?UTF-8?q?=EB=B4=89=EC=82=AC=EC=9E=90=EC=99=80=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 봉사자 등록 시 기본값과 입력값이 올바르게 저장되는지 검증 - Volunteer 엔티티: OAuthProvider, oauthId, nickname 등 기본값 검증 - VolunteerDetail 엔티티: name, email, gender 등 입력값 매핑 검증 --- .../command/RegisterVolunteerServiceTest.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java diff --git a/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java new file mode 100644 index 000000000..1b467ec6a --- /dev/null +++ b/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java @@ -0,0 +1,83 @@ +package com.somemore.volunteer.service.command; + +import com.somemore.IntegrationTestSupport; +import com.somemore.auth.oauth.OAuthProvider; +import com.somemore.volunteer.domain.Gender; +import com.somemore.volunteer.domain.Tier; +import com.somemore.volunteer.domain.Volunteer; +import com.somemore.volunteer.domain.VolunteerDetail; +import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; +import com.somemore.volunteer.repository.VolunteerDetailRepository; +import com.somemore.volunteer.repository.VolunteerRepository; +import jakarta.persistence.EntityNotFoundException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@Transactional +class RegisterVolunteerServiceTest extends IntegrationTestSupport { + + @Autowired + private RegisterVolunteerService registerVolunteerService; + + @Autowired + private VolunteerRepository volunteerRepository; + + @Autowired + private VolunteerDetailRepository volunteerDetailRepository; + + @AfterEach + void tearDown() { + volunteerRepository.deleteAllInBatch(); + volunteerDetailRepository.deleteAllInBatch(); + } + + @DisplayName("봉사자와 상세 정보를 저장한다") + @Test + void registerVolunteer() { + // given + VolunteerRegisterRequestDto dto = new VolunteerRegisterRequestDto( + OAuthProvider.NAVER, + "oauth-id-example", + "making", + "making@example.com", + "M", + "06-08", + "1998", + "010-1234-5678" + ); + + // when + registerVolunteerService.registerVolunteer(dto); + + // then + Volunteer volunteer = volunteerRepository.findByOauthId("oauth-id-example") + .orElseThrow(EntityNotFoundException::new); + VolunteerDetail volunteerDetail = volunteerDetailRepository.findByVolunteerId(volunteer.getId()) + .orElseThrow(EntityNotFoundException::new); + + // Volunteer + assertThat(volunteer.getOauthProvider()).isEqualTo(OAuthProvider.NAVER); + assertThat(volunteer.getOauthId()).isEqualTo("oauth-id-example"); + assertThat(volunteer.getNickname()).hasSize(8); // 8자리 default UUID + assertThat(volunteer.getImgUrl()).isEqualTo(""); // default + assertThat(volunteer.getIntroduce()).isEqualTo(""); // default + assertThat(volunteer.getTier()).isEqualTo(Tier.RED); // default + assertThat(volunteer.getTotalVolunteerHours()).isEqualTo(0); // default + assertThat(volunteer.getTotalVolunteerCount()).isEqualTo(0); // default + + // VolunteerDetail + assertThat(volunteerDetail.getVolunteerId()).isEqualTo(volunteer.getId()); + assertThat(volunteerDetail.getName()).isEqualTo("making"); + assertThat(volunteerDetail.getEmail()).isEqualTo("making@example.com"); + assertThat(volunteerDetail.getGender()).isEqualTo(Gender.MALE); + assertThat(volunteerDetail.getBirthDate()).isEqualTo("1998-06-08"); + assertThat(volunteerDetail.getContactNumber()).isEqualTo("010-1234-5678"); + } +} From 6e9aecb4822bd9f47e6349c567aae92d5efb6715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 01:30:12 +0900 Subject: [PATCH 40/49] =?UTF-8?q?style(=EA=B0=9C=ED=96=89):=20git=20?= =?UTF-8?q?=EC=9E=A0=EC=9E=AC=EC=A0=81=EC=9D=B8=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=98=88=EB=B0=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/somemore/auth/oauth/OAuthProvider.java | 2 +- .../oauth/naver/dto/response/NaverUserProfileResponseDto.java | 2 +- .../somemore/auth/oauth/service/CustomOAuth2UserService.java | 2 +- .../somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java | 2 +- src/main/java/com/somemore/auth/redirect/RedirectUseCase.java | 2 +- .../volunteer/repository/VolunteerDetailRepository.java | 1 + src/main/resources/application.yml | 2 +- 7 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/somemore/auth/oauth/OAuthProvider.java b/src/main/java/com/somemore/auth/oauth/OAuthProvider.java index c902aead5..bf0a73914 100644 --- a/src/main/java/com/somemore/auth/oauth/OAuthProvider.java +++ b/src/main/java/com/somemore/auth/oauth/OAuthProvider.java @@ -21,4 +21,4 @@ public static OAuthProvider from(String providerName) { throw new IllegalArgumentException("올바르지 않은 OAuth 제공자: " + providerName); } -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/auth/oauth/naver/dto/response/NaverUserProfileResponseDto.java b/src/main/java/com/somemore/auth/oauth/naver/dto/response/NaverUserProfileResponseDto.java index cf512d301..0d03ca360 100644 --- a/src/main/java/com/somemore/auth/oauth/naver/dto/response/NaverUserProfileResponseDto.java +++ b/src/main/java/com/somemore/auth/oauth/naver/dto/response/NaverUserProfileResponseDto.java @@ -35,4 +35,4 @@ public VolunteerRegisterRequestDto toVolunteerRegisterRequestDto() { this.response.mobile() ); } -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/auth/oauth/service/CustomOAuth2UserService.java b/src/main/java/com/somemore/auth/oauth/service/CustomOAuth2UserService.java index 087fa0c44..dc8035035 100644 --- a/src/main/java/com/somemore/auth/oauth/service/CustomOAuth2UserService.java +++ b/src/main/java/com/somemore/auth/oauth/service/CustomOAuth2UserService.java @@ -36,4 +36,4 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic private static String getRegistrationId(OAuth2UserRequest userRequest) { return userRequest.getClientRegistration().getRegistrationId(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java b/src/main/java/com/somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java index acc1fcaa7..5bbf14924 100644 --- a/src/main/java/com/somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java +++ b/src/main/java/com/somemore/auth/oauth/usecase/ProcessOAuthUserUseCase.java @@ -7,4 +7,4 @@ public interface ProcessOAuthUserUseCase { String processOAuthUser(Authentication authentication) throws IOException, ServletException; -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/auth/redirect/RedirectUseCase.java b/src/main/java/com/somemore/auth/redirect/RedirectUseCase.java index 9f9e86153..d215c4ce8 100644 --- a/src/main/java/com/somemore/auth/redirect/RedirectUseCase.java +++ b/src/main/java/com/somemore/auth/redirect/RedirectUseCase.java @@ -6,4 +6,4 @@ public interface RedirectUseCase { void redirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException; -} \ No newline at end of file +} diff --git a/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java b/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java index 82e8f96f7..7dcb9f3c1 100644 --- a/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java +++ b/src/main/java/com/somemore/volunteer/repository/VolunteerDetailRepository.java @@ -7,5 +7,6 @@ import java.util.UUID; public interface VolunteerDetailRepository extends JpaRepository { + Optional findByVolunteerId(UUID volunteerId); } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d6281a999..e6943650b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -66,4 +66,4 @@ jwt: logging: level: - org.springframework.security: DEBUG \ No newline at end of file + org.springframework.security: DEBUG From f38391e656536d13ec5458ab786fffb8600f48ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:02:46 +0900 Subject: [PATCH 41/49] =?UTF-8?q?feat(OAuthResponseConverter):=20=EC=9C=A0?= =?UTF-8?q?=ED=8B=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B8=B0=EB=B3=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20private=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/oauth/naver/util/OAuthResponseConverter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java b/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java index 180508305..94000e64c 100644 --- a/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java +++ b/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java @@ -7,6 +7,10 @@ import java.util.Map; public class OAuthResponseConverter { + + private OAuthResponseConverter(){ + } + public static NaverUserProfileResponseDto convertToNaverUserProfileResponseDto(OAuth2User oAuth2User) { ObjectMapper objectMapper = new ObjectMapper(); From d1a5dafb821021dfd8258446dea765654d745014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:03:04 +0900 Subject: [PATCH 42/49] =?UTF-8?q?fix(VolunteerRepository):=20=EB=8C=80?= =?UTF-8?q?=EC=86=8C=EB=AC=B8=EC=9E=90=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/volunteer/repository/VolunteerRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/volunteer/repository/VolunteerRepository.java b/src/main/java/com/somemore/volunteer/repository/VolunteerRepository.java index 90140733f..25e15dde8 100644 --- a/src/main/java/com/somemore/volunteer/repository/VolunteerRepository.java +++ b/src/main/java/com/somemore/volunteer/repository/VolunteerRepository.java @@ -10,5 +10,5 @@ @Repository public interface VolunteerRepository extends JpaRepository { - Optional findByOauthId(String OauthId); + Optional findByOauthId(String oauthId); } From 5b223a143846f27125557f66f923ed0ac4302655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:17:07 +0900 Subject: [PATCH 43/49] =?UTF-8?q?style(=EA=B0=9C=ED=96=89):=20git=20?= =?UTF-8?q?=EC=9E=A0=EC=9E=AC=EC=A0=81=EC=9D=B8=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=98=88=EB=B0=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index 55537ee8e..5b20e92d0 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -31,4 +31,4 @@ frontend: url: http://localhost:3000 # 테스트용 프론트엔드 주소 jwt: - secret: 63bf2c80266cd25072e53b3482e318c30d1cd18d8c98d0f5d278530a94fe28d9fbbec531e5ccb58c725c125738182357786b71f43a7172c5d0c94a17f0da44f2 # 테스트용 JWT 시크릿 키 \ No newline at end of file + secret: 63bf2c80266cd25072e53b3482e318c30d1cd18d8c98d0f5d278530a94fe28d9fbbec531e5ccb58c725c125738182357786b71f43a7172c5d0c94a17f0da44f2 # 테스트용 JWT 시크릿 키 From 67575e29efa2b6595046af8f02c231ae27731901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:35:03 +0900 Subject: [PATCH 44/49] =?UTF-8?q?feat(SecurityConfig):=20=EB=AA=A8?= =?UTF-8?q?=EB=93=A0=20=EC=9A=94=EC=B2=AD=20permitAll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 빠른 개발을 위해서 모든 요청 허가 --- .../global/configure/SecurityConfig.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/somemore/global/configure/SecurityConfig.java b/src/main/java/com/somemore/global/configure/SecurityConfig.java index a044d7300..bc7edb98c 100644 --- a/src/main/java/com/somemore/global/configure/SecurityConfig.java +++ b/src/main/java/com/somemore/global/configure/SecurityConfig.java @@ -36,17 +36,19 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(request -> - request - .requestMatchers( - "/login", - "/oauth2/**", - "/api/auth/**", - "/v3/api-docs/**", - "/swagger/**", - "/swagger-ui.html", - "/swagger-ui/**") - .permitAll() - .anyRequest().authenticated() + request + .requestMatchers( + "/**" +// "/login", +// "/oauth2/**", +// "/api/auth/**", +// "/v3/api-docs/**", +// "/swagger/**", +// "/swagger-ui.html", +// "/swagger-ui/**" + ) + .permitAll() + .anyRequest().authenticated() ) .oauth2Login(oauth2 -> From 455fe76aba301f1c1cc39dcbfac623250462c9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:35:43 +0900 Subject: [PATCH 45/49] =?UTF-8?q?fix(Test):=20=EC=A4=91=EB=B3=B5=EB=90=9C?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=84=A4=EC=A0=95=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/command/RegisterVolunteerServiceTest.java | 4 ---- .../volunteer/service/query/FindVolunteerIdServiceTest.java | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java index 1b467ec6a..8454a470d 100644 --- a/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java @@ -14,13 +14,9 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; -@SpringBootTest -@Transactional class RegisterVolunteerServiceTest extends IntegrationTestSupport { @Autowired diff --git a/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java b/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java index 88b03e787..29aed5992 100644 --- a/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java @@ -9,14 +9,12 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -@SpringBootTest class FindVolunteerIdServiceTest extends IntegrationTestSupport { @Autowired From 8d0fd8d93a5d7c67fc16f05e4e8a29b6dcfd4ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:37:04 +0900 Subject: [PATCH 46/49] =?UTF-8?q?refactor(OAuthResponseConverter):=20priva?= =?UTF-8?q?te=20=EA=B8=B0=EB=B3=B8=20=EC=83=9D=EC=84=B1=EC=9E=90=EB=A5=BC?= =?UTF-8?q?=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/oauth/naver/util/OAuthResponseConverter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java b/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java index 94000e64c..13fe95c44 100644 --- a/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java +++ b/src/main/java/com/somemore/auth/oauth/naver/util/OAuthResponseConverter.java @@ -2,15 +2,15 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.somemore.auth.oauth.naver.dto.response.NaverUserProfileResponseDto; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.springframework.security.oauth2.core.user.OAuth2User; import java.util.Map; +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class OAuthResponseConverter { - private OAuthResponseConverter(){ - } - public static NaverUserProfileResponseDto convertToNaverUserProfileResponseDto(OAuth2User oAuth2User) { ObjectMapper objectMapper = new ObjectMapper(); From f6ab84fbc37c328ffee06a6fcde5ea52cc73c9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:26:45 +0900 Subject: [PATCH 47/49] =?UTF-8?q?feat(Voluntter):=20updatable=20=3D=20fals?= =?UTF-8?q?e=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/somemore/volunteer/domain/Volunteer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/volunteer/domain/Volunteer.java b/src/main/java/com/somemore/volunteer/domain/Volunteer.java index 4a8d78b9d..a8125c9c7 100644 --- a/src/main/java/com/somemore/volunteer/domain/Volunteer.java +++ b/src/main/java/com/somemore/volunteer/domain/Volunteer.java @@ -14,7 +14,7 @@ public class Volunteer extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.UUID) - @Column(name = "id", nullable = false, updatable = false, columnDefinition = "BINARY(16)") + @Column(name = "id", nullable = false, columnDefinition = "BINARY(16)") private UUID id; @Enumerated(EnumType.STRING) From 0338697f4aa736bd491309a2a7b53f68196a07d6 Mon Sep 17 00:00:00 2001 From: seojin Yoon <90759319+7zrv@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:47:14 +0900 Subject: [PATCH 48/49] =?UTF-8?q?Feature/9=20=EA=B8=B0=EA=B4=80=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 예외 메시지 상수를 enum으로 관리하도록 개선 - 예외 메시지를 ExceptionMessage enum으로 분리하여 중앙 관리 - 상수로 관리하여 메시지의 일관성 유지 및 중복 제거 - 향후 메시지 변경 시 한 곳에서 관리 가능하도록 개선 * feat: 선호물품 등록 기능 구현 - 기관이 선호하는 물품을 등록할 수 있는 API 추가 - 선호물품 테이블 추가 - 선호물품 등록 요청 DTO 구현 - 선호물품 등록 서비스 레이어 구현 - 테스트 코드 작성 및 검증 완료 * feat: 기관 존재 여부 검증 기능 추가 - 기관 존재 여부를 확인하는 validateCenterExists 메서드 구현 - 존재하지 않는 센터에 대해 BadRequestException 처리 추가 - 테스트 코드 작성및 검증 완 * feat: 기관 프로필 조회 기능 구현 - 기관 id로 기관 정보와 기관이 등록한 선호 물품을 조회하는 메서드 구현 - 레포지토리에 기관 id를 통한 단건 조회 메서드 추가 - 기관과 선호물품을 함께 반환하기 위한 응답Dto 구현 - 테스트 코드 작성및 검증 완료 * chore: sonar 커버리지 설정 변경 - 누락된 exclude 설정을 추가 * chore: 파일 마지막 라인에 개행 추가 - 코딩 표준을 준수하기 위해 "no newline at end of file" 경고를 수정했습니다. * Fix: 코드 리뷰 사항 반영 - 서비스 레이어의 public 메서드를 상단에 배치하여 가독성 향상 - CQRS 적용을 위한 Query, Command 서비스 클래스에 @Transactinal(readOnly = true), @Transactinal 어노테이션을 각각 추가 - 레포지토리의 불필요한 @Query 어노테이션 삭제 * feat: 예외 메시지 상수를 enum으로 관리하도록 개선 - 예외 메시지를 ExceptionMessage enum으로 분리하여 중앙 관리 - 상수로 관리하여 메시지의 일관성 유지 및 중복 제거 - 향후 메시지 변경 시 한 곳에서 관리 가능하도록 개선 * feat: 선호물품 등록 기능 구현 - 기관이 선호하는 물품을 등록할 수 있는 API 추가 - 선호물품 테이블 추가 - 선호물품 등록 요청 DTO 구현 - 선호물품 등록 서비스 레이어 구현 - 테스트 코드 작성 및 검증 완료 * feat: 기관 존재 여부 검증 기능 추가 - 기관 존재 여부를 확인하는 validateCenterExists 메서드 구현 - 존재하지 않는 센터에 대해 BadRequestException 처리 추가 - 테스트 코드 작성및 검증 완 * feat: 기관 프로필 조회 기능 구현 - 기관 id로 기관 정보와 기관이 등록한 선호 물품을 조회하는 메서드 구현 - 레포지토리에 기관 id를 통한 단건 조회 메서드 추가 - 기관과 선호물품을 함께 반환하기 위한 응답Dto 구현 - 테스트 코드 작성및 검증 완료 * chore: sonar 커버리지 설정 변경 - 누락된 exclude 설정을 추가 * chore: 파일 마지막 라인에 개행 추가 - 코딩 표준을 준수하기 위해 "no newline at end of file" 경고를 수정했습니다. * Fix: 코드 리뷰 사항 반영 - 서비스 레이어의 public 메서드를 상단에 배치하여 가독성 향상 - CQRS 적용을 위한 Query, Command 서비스 클래스에 @Transactinal(readOnly = true), @Transactinal 어노테이션을 각각 추가 - 레포지토리의 불필요한 @Query 어노테이션 삭제 * Refactor: 코드 리뷰 사항 반영 - 기관 프로필 조회 응답값중 선호 물품 엔티티를 직접 응답하는것이 아닌 Dto로 전환하여 응답하도록 수정과 그에 따른 usecase, service 수정, 선호물품 응답 Dto 생성 - 단위 테스트 코드 작성과 검증 완료 --- .../com/somemore/center/domain/Center.java | 66 ++++++++++ .../somemore/center/domain/PreferItem.java | 38 ++++++ .../request/PreferItemCreateRequestDto.java | 19 +++ .../response/CenterProfileResponseDto.java | 34 +++++ .../dto/response/PreferItemResponseDto.java | 21 +++ .../center/repository/CenterRepository.java | 20 +++ .../repository/PreferItemRepository.java | 15 +++ .../command/CreatePreferItemService.java | 27 ++++ .../service/query/CenterQueryService.java | 48 +++++++ .../service/query/PreferItemQueryService.java | 38 ++++++ .../command/CreatePreferItemUseCase.java | 8 ++ .../usecase/query/CenterQueryUseCase.java | 11 ++ .../usecase/query/PreferItemQueryUseCase.java | 12 ++ .../java/com/somemore/domains/Center.java | 42 ------ .../global/exception/BadRequestException.java | 6 +- .../global/exception/ExceptionMessage.java | 15 +++ .../somemore/center/domain/CenterTest.java | 46 +++++++ .../repository/CenterRepositoryTest.java | 85 ++++++++++++ .../repository/PreferItemRepositoryTest.java | 48 +++++++ .../command/CreatePreferItemServiceTest.java | 64 +++++++++ .../service/query/CenterQueryServiceTest.java | 122 ++++++++++++++++++ .../query/PreferItemQueryServiceTest.java | 75 +++++++++++ 22 files changed, 813 insertions(+), 47 deletions(-) create mode 100644 src/main/java/com/somemore/center/domain/Center.java create mode 100644 src/main/java/com/somemore/center/domain/PreferItem.java create mode 100644 src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java create mode 100644 src/main/java/com/somemore/center/dto/response/CenterProfileResponseDto.java create mode 100644 src/main/java/com/somemore/center/dto/response/PreferItemResponseDto.java create mode 100644 src/main/java/com/somemore/center/repository/CenterRepository.java create mode 100644 src/main/java/com/somemore/center/repository/PreferItemRepository.java create mode 100644 src/main/java/com/somemore/center/service/command/CreatePreferItemService.java create mode 100644 src/main/java/com/somemore/center/service/query/CenterQueryService.java create mode 100644 src/main/java/com/somemore/center/service/query/PreferItemQueryService.java create mode 100644 src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java create mode 100644 src/main/java/com/somemore/center/usecase/query/CenterQueryUseCase.java create mode 100644 src/main/java/com/somemore/center/usecase/query/PreferItemQueryUseCase.java delete mode 100644 src/main/java/com/somemore/domains/Center.java create mode 100644 src/main/java/com/somemore/global/exception/ExceptionMessage.java create mode 100644 src/test/java/com/somemore/center/domain/CenterTest.java create mode 100644 src/test/java/com/somemore/center/repository/CenterRepositoryTest.java create mode 100644 src/test/java/com/somemore/center/repository/PreferItemRepositoryTest.java create mode 100644 src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java create mode 100644 src/test/java/com/somemore/center/service/query/CenterQueryServiceTest.java create mode 100644 src/test/java/com/somemore/center/service/query/PreferItemQueryServiceTest.java diff --git a/src/main/java/com/somemore/center/domain/Center.java b/src/main/java/com/somemore/center/domain/Center.java new file mode 100644 index 000000000..a13f964f2 --- /dev/null +++ b/src/main/java/com/somemore/center/domain/Center.java @@ -0,0 +1,66 @@ +package com.somemore.center.domain; + +import com.somemore.global.common.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +import java.util.UUID; + + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@Entity +public class Center extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + @Column(nullable = false, length = 16) + private UUID id; + + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "contact_number", nullable = false) + private String contactNumber; + + @Column(name = "img_url", nullable = false) + private String imgUrl; + + @Column(name = "introduce", columnDefinition = "TEXT", nullable = false) + private String introduce; + + @Column(name = "homepage_link", nullable = false) + private String homepageLink; + + @Column(name = "account_id", nullable = false) + private String accountId; + + @Column(name = "account_pw", nullable = false) + private String accountPw; + + + @Builder + private Center(String name, String contactNumber, String imgUrl, String introduce, String homepageLink, String accountId, String accountPw) { + + this.name = name; + this.contactNumber = contactNumber; + this.imgUrl = imgUrl; + this.introduce = introduce; + this.homepageLink = homepageLink; + this.accountId = accountId; + this.accountPw = accountPw; + } + + public static Center create(String name, String contactNumber, String imgUrl, String introduce, String homepageLink, String accountId, String accountPw) { + return Center.builder() + .name(name) + .contactNumber(contactNumber) + .imgUrl(imgUrl) + .introduce(introduce) + .homepageLink(homepageLink) + .accountId(accountId) + .accountPw(accountPw) + .build(); + } + +} diff --git a/src/main/java/com/somemore/center/domain/PreferItem.java b/src/main/java/com/somemore/center/domain/PreferItem.java new file mode 100644 index 000000000..9cc935974 --- /dev/null +++ b/src/main/java/com/somemore/center/domain/PreferItem.java @@ -0,0 +1,38 @@ +package com.somemore.center.domain; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@Entity +public class PreferItem { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "center_id", nullable = false) + private UUID centerId; + + @Column(name = "item_name", nullable = false) + private String itemName; + + @Builder + private PreferItem(UUID centerId, String itemName) { + this.centerId = centerId; + this.itemName = itemName; + } + + public static PreferItem create(UUID centerId, String itemName) { + return PreferItem.builder() + .centerId(centerId) + .itemName(itemName) + .build(); + } +} diff --git a/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java b/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java new file mode 100644 index 000000000..bda87dae5 --- /dev/null +++ b/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java @@ -0,0 +1,19 @@ +package com.somemore.center.dto.request; + + +import com.somemore.center.domain.PreferItem; + +import java.util.UUID; + + +public record PreferItemCreateRequestDto( + + UUID centerId, + String itemName + +) { + public PreferItem createPreferItem() { + return PreferItem.create(centerId, itemName); + } + +} diff --git a/src/main/java/com/somemore/center/dto/response/CenterProfileResponseDto.java b/src/main/java/com/somemore/center/dto/response/CenterProfileResponseDto.java new file mode 100644 index 000000000..7ccf73d63 --- /dev/null +++ b/src/main/java/com/somemore/center/dto/response/CenterProfileResponseDto.java @@ -0,0 +1,34 @@ +package com.somemore.center.dto.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.somemore.center.domain.Center; +import com.somemore.center.domain.PreferItem; +import lombok.Builder; + +import java.util.List; +import java.util.UUID; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@Builder +public record CenterProfileResponseDto( + UUID centerId, + String name, + String contactNumber, + String imgUrl, + String introduce, + String homepageLink, + List preferItems +) { + public static CenterProfileResponseDto of(Center center, List preferItemDtos) { + return CenterProfileResponseDto.builder() + .centerId(center.getId()) + .name(center.getName()) + .contactNumber(center.getContactNumber()) + .imgUrl(center.getImgUrl()) + .introduce(center.getIntroduce()) + .homepageLink(center.getHomepageLink()) + .preferItems(preferItemDtos) + .build(); + } +} diff --git a/src/main/java/com/somemore/center/dto/response/PreferItemResponseDto.java b/src/main/java/com/somemore/center/dto/response/PreferItemResponseDto.java new file mode 100644 index 000000000..85fa74744 --- /dev/null +++ b/src/main/java/com/somemore/center/dto/response/PreferItemResponseDto.java @@ -0,0 +1,21 @@ +package com.somemore.center.dto.response; + +import com.somemore.center.domain.PreferItem; +import lombok.Builder; + +import java.util.UUID; + +@Builder +public record PreferItemResponseDto ( + Long id, + UUID centerId, + String itemName +){ + public static PreferItemResponseDto from(PreferItem preferItem) { + return PreferItemResponseDto.builder() + .id(preferItem.getId()) + .centerId(preferItem.getCenterId()) + .itemName(preferItem.getItemName()) + .build(); + } +} diff --git a/src/main/java/com/somemore/center/repository/CenterRepository.java b/src/main/java/com/somemore/center/repository/CenterRepository.java new file mode 100644 index 000000000..b72572f5a --- /dev/null +++ b/src/main/java/com/somemore/center/repository/CenterRepository.java @@ -0,0 +1,20 @@ +package com.somemore.center.repository; + +import com.somemore.center.domain.Center; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface CenterRepository extends JpaRepository { + + boolean existsById(UUID id); + + default boolean doesNotExistById(UUID id) { + return !existsById(id); + } + + Optional
findCenterById(UUID id); +} diff --git a/src/main/java/com/somemore/center/repository/PreferItemRepository.java b/src/main/java/com/somemore/center/repository/PreferItemRepository.java new file mode 100644 index 000000000..8f3f4eecb --- /dev/null +++ b/src/main/java/com/somemore/center/repository/PreferItemRepository.java @@ -0,0 +1,15 @@ +package com.somemore.center.repository; + +import com.somemore.center.domain.PreferItem; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.UUID; + +@Repository +public interface PreferItemRepository extends JpaRepository { + + List findByCenterId(UUID centerId); + +} diff --git a/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java b/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java new file mode 100644 index 000000000..5a22f8679 --- /dev/null +++ b/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java @@ -0,0 +1,27 @@ +package com.somemore.center.service.command; + +import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.repository.PreferItemRepository; +import com.somemore.center.usecase.command.CreatePreferItemUseCase; +import com.somemore.center.usecase.query.CenterQueryUseCase; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Transactional +@Service +public class CreatePreferItemService implements CreatePreferItemUseCase { + + private final CenterQueryUseCase centerQueryUseCase; + private final PreferItemRepository preferItemRepository; + + @Override + public void createPreferItem(PreferItemCreateRequestDto requestDto) { + + centerQueryUseCase.validateCenterExists(requestDto.centerId()); + + preferItemRepository.save(requestDto.createPreferItem()); + } + +} diff --git a/src/main/java/com/somemore/center/service/query/CenterQueryService.java b/src/main/java/com/somemore/center/service/query/CenterQueryService.java new file mode 100644 index 000000000..838eba6ea --- /dev/null +++ b/src/main/java/com/somemore/center/service/query/CenterQueryService.java @@ -0,0 +1,48 @@ +package com.somemore.center.service.query; + +import com.somemore.center.domain.Center; +import com.somemore.center.dto.response.CenterProfileResponseDto; +import com.somemore.center.dto.response.PreferItemResponseDto; +import com.somemore.center.repository.CenterRepository; +import com.somemore.center.usecase.query.CenterQueryUseCase; +import com.somemore.center.usecase.query.PreferItemQueryUseCase; +import com.somemore.global.exception.BadRequestException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_CENTER; + +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Service +public class CenterQueryService implements CenterQueryUseCase { + + private final PreferItemQueryUseCase preferItemQueryUseCase; + private final CenterRepository centerRepository; + + @Override + public CenterProfileResponseDto getCenterProfileByCenterId(UUID centerId) { + + Center center = getCenterById(centerId); + List preferItemDtos = preferItemQueryUseCase.getPreferItemDtosByCenterId(centerId); + + return CenterProfileResponseDto.of(center, preferItemDtos); + } + + @Override + public void validateCenterExists(UUID id) { + if (centerRepository.doesNotExistById(id)) { + throw new BadRequestException(NOT_EXISTS_CENTER.getMessage()); + } + } + + private Center getCenterById(UUID centerId) { + return centerRepository.findCenterById(centerId) + .orElseThrow(() -> new BadRequestException(NOT_EXISTS_CENTER.getMessage())); + } + +} diff --git a/src/main/java/com/somemore/center/service/query/PreferItemQueryService.java b/src/main/java/com/somemore/center/service/query/PreferItemQueryService.java new file mode 100644 index 000000000..46074e0a5 --- /dev/null +++ b/src/main/java/com/somemore/center/service/query/PreferItemQueryService.java @@ -0,0 +1,38 @@ +package com.somemore.center.service.query; + +import com.somemore.center.domain.PreferItem; +import com.somemore.center.dto.response.PreferItemResponseDto; +import com.somemore.center.repository.PreferItemRepository; +import com.somemore.center.usecase.query.PreferItemQueryUseCase; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Service +public class PreferItemQueryService implements PreferItemQueryUseCase { + + private final PreferItemRepository preferItemRepository; + + @Override + public List getPreferItemDtosByCenterId(UUID centerId) { + List preferItems = getPreferItemsByCenterId(centerId); + return preferItemConvertToDtos(preferItems); + } + + //프론트와 의논후 private으로 전환 예정 + @Override + public List getPreferItemsByCenterId(UUID centerId) { + return preferItemRepository.findByCenterId(centerId); + } + + private static List preferItemConvertToDtos(List preferItems) { + return preferItems.stream() + .map(PreferItemResponseDto::from) + .toList(); + } +} diff --git a/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java b/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java new file mode 100644 index 000000000..78c793edc --- /dev/null +++ b/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java @@ -0,0 +1,8 @@ +package com.somemore.center.usecase.command; + +import com.somemore.center.dto.request.PreferItemCreateRequestDto; + +public interface CreatePreferItemUseCase { + + void createPreferItem(PreferItemCreateRequestDto requestDto); +} diff --git a/src/main/java/com/somemore/center/usecase/query/CenterQueryUseCase.java b/src/main/java/com/somemore/center/usecase/query/CenterQueryUseCase.java new file mode 100644 index 000000000..7c12cb28c --- /dev/null +++ b/src/main/java/com/somemore/center/usecase/query/CenterQueryUseCase.java @@ -0,0 +1,11 @@ +package com.somemore.center.usecase.query; + +import com.somemore.center.dto.response.CenterProfileResponseDto; + +import java.util.UUID; + +public interface CenterQueryUseCase { + + CenterProfileResponseDto getCenterProfileByCenterId(UUID centerId); + void validateCenterExists(UUID centerId); +} diff --git a/src/main/java/com/somemore/center/usecase/query/PreferItemQueryUseCase.java b/src/main/java/com/somemore/center/usecase/query/PreferItemQueryUseCase.java new file mode 100644 index 000000000..46230cfc9 --- /dev/null +++ b/src/main/java/com/somemore/center/usecase/query/PreferItemQueryUseCase.java @@ -0,0 +1,12 @@ +package com.somemore.center.usecase.query; + +import com.somemore.center.domain.PreferItem; +import com.somemore.center.dto.response.PreferItemResponseDto; + +import java.util.List; +import java.util.UUID; + +public interface PreferItemQueryUseCase { + List getPreferItemDtosByCenterId(UUID centerId); + List getPreferItemsByCenterId(UUID centerId); +} diff --git a/src/main/java/com/somemore/domains/Center.java b/src/main/java/com/somemore/domains/Center.java deleted file mode 100644 index 0fd91979d..000000000 --- a/src/main/java/com/somemore/domains/Center.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.somemore.domains; - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.Setter; - -import java.util.UUID; - -@Getter -@Setter -@Entity -public class Center { - @Id - @GeneratedValue(strategy = GenerationType.UUID) - private UUID id; - - @Column(name = "name", nullable = false) - private String name; - - @Column(name = "contact_number", nullable = false) - private String contactNumber; - - @Column(name = "img_url", nullable = false) - private String imgUrl; - - @Lob - @Column(name = "introduce", nullable = false) - private String introduce; - - @Column(name = "requirement") - private String requirement; - - @Column(name = "homepage_link") - private String homepageLink; - - @Column(name = "account_id", nullable = false) - private String accountId; - - @Column(name = "account_pw", nullable = false) - private String accountPw; - -} \ No newline at end of file diff --git a/src/main/java/com/somemore/global/exception/BadRequestException.java b/src/main/java/com/somemore/global/exception/BadRequestException.java index 3066752fe..488546df3 100644 --- a/src/main/java/com/somemore/global/exception/BadRequestException.java +++ b/src/main/java/com/somemore/global/exception/BadRequestException.java @@ -4,11 +4,7 @@ public class BadRequestException extends RuntimeException{ - public BadRequestException() { - super(); - } - - public BadRequestException(String message) { + public BadRequestException(final String message) { super(message); } } diff --git a/src/main/java/com/somemore/global/exception/ExceptionMessage.java b/src/main/java/com/somemore/global/exception/ExceptionMessage.java new file mode 100644 index 000000000..615f84ac0 --- /dev/null +++ b/src/main/java/com/somemore/global/exception/ExceptionMessage.java @@ -0,0 +1,15 @@ +package com.somemore.global.exception; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public enum ExceptionMessage { + + NOT_EXISTS_CENTER("존재하지 않는 기관 ID 입니다.") + ; + + private final String message; +} diff --git a/src/test/java/com/somemore/center/domain/CenterTest.java b/src/test/java/com/somemore/center/domain/CenterTest.java new file mode 100644 index 000000000..bed4b8583 --- /dev/null +++ b/src/test/java/com/somemore/center/domain/CenterTest.java @@ -0,0 +1,46 @@ +package com.somemore.center.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class CenterTest { + + private static final String NAME = "서울 도서관"; + private static final String CONTACT_NUMBER = "02-1234-5678"; + private static final String IMG_URL = "http://example.com/image.jpg"; + private static final String INTRODUCE = "우리는 서울 도서관입니다."; + private static final String HOMEPAGE_LINK = "http://testcenter.com"; + private static final String ACCOUNT_ID = "testId"; + private static final String ACCOUNT_PW = "testPw"; + + @DisplayName("Center 엔티티를 생성할 수 있다.") + @Test + void create() { + //when + Center center = Center.create( + NAME, + CONTACT_NUMBER, + IMG_URL, + INTRODUCE, + HOMEPAGE_LINK, + ACCOUNT_ID, + ACCOUNT_PW + ); + + //then + assertAll( + () -> assertThat(center.getName()).isEqualTo(NAME), + () -> assertThat(center.getContactNumber()).isEqualTo(CONTACT_NUMBER), + () -> assertThat(center.getImgUrl()).isEqualTo(IMG_URL), + () -> assertThat(center.getIntroduce()).isEqualTo(INTRODUCE), + () -> assertThat(center.getHomepageLink()).isEqualTo(HOMEPAGE_LINK), + () -> assertThat(center.getAccountId()).isEqualTo(ACCOUNT_ID), + () -> assertThat(center.getAccountPw()).isEqualTo(ACCOUNT_PW) + ); + } + + +} \ No newline at end of file diff --git a/src/test/java/com/somemore/center/repository/CenterRepositoryTest.java b/src/test/java/com/somemore/center/repository/CenterRepositoryTest.java new file mode 100644 index 000000000..cb663d659 --- /dev/null +++ b/src/test/java/com/somemore/center/repository/CenterRepositoryTest.java @@ -0,0 +1,85 @@ +package com.somemore.center.repository; + +import com.somemore.IntegrationTestSupport; +import com.somemore.center.domain.Center; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Optional; +import java.util.UUID; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@Transactional +class CenterRepositoryTest extends IntegrationTestSupport { + + @Autowired + private CenterRepository centerRepository; + + @DisplayName("기관 id로 기관 정보를 조회할 수 있다. (Repository)") + @Test + void findById() { + //given + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + centerRepository.save(center); + + //when + Optional
foundCenter = centerRepository.findCenterById(center.getId()); + + //then + assertThat(foundCenter).isNotNull(); + assertThat(foundCenter.get().getName()).isEqualTo("기본 기관 이름"); + assertThat(foundCenter.get().getContactNumber()).isEqualTo("010-1234-5678"); + assertThat(foundCenter.get().getImgUrl()).isEqualTo("http://example.com/image.jpg"); + assertThat(foundCenter.get().getIntroduce()).isEqualTo("기관 소개 내용"); + assertThat(foundCenter.get().getHomepageLink()).isEqualTo("http://example.com"); + assertThat(foundCenter.get().getAccountId()).isEqualTo("account123"); + assertThat(foundCenter.get().getAccountPw()).isEqualTo("password123"); + } + + @DisplayName("기관 id로 기관이 존재하는지 확인할 수 있다.") + @Test + void existsById() { + //given + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + centerRepository.save(center); + + //when + Boolean isExist = centerRepository.existsById(center.getId()); + + //then + assertThat(isExist).isTrue(); + } + + @DisplayName("존재하지 않는 기관 ID로 조회 시 false를 반환한다") + @Test + void notExistsById() { + //given + UUID nonExistentId = UUID.randomUUID(); + + //when + Boolean isExist = centerRepository.existsById(nonExistentId); + + //then + assertThat(isExist).isFalse(); + } + +} diff --git a/src/test/java/com/somemore/center/repository/PreferItemRepositoryTest.java b/src/test/java/com/somemore/center/repository/PreferItemRepositoryTest.java new file mode 100644 index 000000000..cf7fcc9d5 --- /dev/null +++ b/src/test/java/com/somemore/center/repository/PreferItemRepositoryTest.java @@ -0,0 +1,48 @@ +package com.somemore.center.repository; + +import com.somemore.IntegrationTestSupport; +import com.somemore.center.domain.PreferItem; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.tuple; + +class PreferItemRepositoryTest extends IntegrationTestSupport { + + @Autowired + private PreferItemRepository preferItemRepository; + + + private UUID centerId = UUID.fromString("1a1a1a1a-1a1a-1a1a-1a1a-1a1a1a1a1a1"); + private UUID centerId1 = UUID.fromString("1a1a1a1a-1a1a-1a1a-1a1a-1a1a1a1a1a1"); + private UUID centerId2 = UUID.fromString("1a1a1a1a-1a1a-1a1a-1a1a-1a1a1a1a1a2"); + private UUID centerId3 = UUID.fromString("1a1a1a1a-1a1a-1a1a-1a1a-1a1a1a1a1a3"); + + @DisplayName("기관의 id로 선호물품을 검색할 수 있다.") + @Test + void findByCenterId() { + + //given + PreferItem preferItem = PreferItem.create(centerId, "어린이 동화책"); + PreferItem preferItem1 = PreferItem.create(centerId1, "간식"); + PreferItem preferItem2 = PreferItem.create(centerId2, "수건"); + PreferItem preferItem3 = PreferItem.create(centerId3, "식재료"); + preferItemRepository.saveAll(List.of(preferItem, preferItem1, preferItem2, preferItem3)); + + //when + List preferItems = preferItemRepository.findByCenterId(UUID.fromString("1a1a1a1a-1a1a-1a1a-1a1a-1a1a1a1a1a2")); + + //then + assertThat(preferItems).hasSize(1) + .extracting("centerId", "itemName") + .containsExactlyInAnyOrder( + tuple(UUID.fromString("1a1a1a1a-1a1a-1a1a-1a1a-1a1a1a1a1a2"), "수건") + ); + } + +} diff --git a/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java b/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java new file mode 100644 index 000000000..a7db8c925 --- /dev/null +++ b/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java @@ -0,0 +1,64 @@ +package com.somemore.center.service.command; + +import com.somemore.IntegrationTestSupport; +import com.somemore.center.domain.Center; +import com.somemore.center.domain.PreferItem; +import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.repository.CenterRepository; +import com.somemore.center.repository.PreferItemRepository; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.assertj.core.api.Assertions.assertThat; + +@Transactional +class CreatePreferItemServiceTest extends IntegrationTestSupport { + + @Autowired + private CreatePreferItemService createPreferItemService; + + @Autowired + private PreferItemRepository preferItemRepository; + + @Autowired + private CenterRepository centerRepository; + + @DisplayName("기관 아이디와 선호물품 이름을 받아 선호물품을 생성한다.") + @Test + void createPreferItem() { + //given + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + centerRepository.save(center); + String itemName = "어린이 도서"; + + + PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto( + center.getId(), + itemName + ); + + //when + createPreferItemService.createPreferItem(requestDto); + + //then + PreferItem savedItem = preferItemRepository.findAll().stream() + .filter(item -> item.getCenterId().equals(center.getId()) + && item.getItemName().equals(itemName)) + .findFirst() + .orElseThrow(() -> new AssertionError("저장된 선호물품을 찾을 수 없습니다.")); + + assertThat(savedItem.getCenterId()).isEqualTo(center.getId()); + assertThat(savedItem.getItemName()).isEqualTo(itemName); + } + +} diff --git a/src/test/java/com/somemore/center/service/query/CenterQueryServiceTest.java b/src/test/java/com/somemore/center/service/query/CenterQueryServiceTest.java new file mode 100644 index 000000000..457b0a175 --- /dev/null +++ b/src/test/java/com/somemore/center/service/query/CenterQueryServiceTest.java @@ -0,0 +1,122 @@ +package com.somemore.center.service.query; + +import com.somemore.IntegrationTestSupport; +import com.somemore.center.domain.Center; +import com.somemore.center.domain.PreferItem; +import com.somemore.center.dto.response.CenterProfileResponseDto; +import com.somemore.center.repository.CenterRepository; +import com.somemore.center.repository.PreferItemRepository; +import com.somemore.global.exception.BadRequestException; +import com.somemore.global.exception.ExceptionMessage; +import jakarta.transaction.Transactional; +import org.assertj.core.api.ThrowableAssert; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +@Transactional +class CenterQueryServiceTest extends IntegrationTestSupport { + + @Autowired + private CenterQueryService centerQueryService; + + @Autowired + private CenterRepository centerRepository; + + @Autowired + private PreferItemRepository preferItemRepository; + + @DisplayName("기관 Id로 기관 프로필을 조회할 수 있다. (service)") + @Test + void getCenterProfileById() { + // given + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + Center foundCenter = centerRepository.save(center); + + PreferItem preferItem = PreferItem.create(foundCenter.getId(), "어린이 동화책"); + PreferItem preferItem1 = PreferItem.create(foundCenter.getId(), "간식"); + preferItemRepository.saveAll(List.of(preferItem, preferItem1)); + + // when + CenterProfileResponseDto responseDto = centerQueryService.getCenterProfileByCenterId(foundCenter.getId()); + + // then + assertThat(responseDto) + .extracting( + "centerId", + "name", + "contactNumber", + "imgUrl", + "introduce", + "homepageLink" + ) + .containsExactly( + foundCenter.getId(), + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com" + ); + + assertThat(responseDto.preferItems()) + .hasSize(2) + .extracting("itemName") + .containsExactly("어린이 동화책", "간식"); + } + + + @DisplayName("존재하지 않는 기관 ID를 검증할 수 있다.") + @Test + void validateNonExistentCenter() { + // given + UUID nonExistentId = UUID.randomUUID(); + + // when + ThrowableAssert.ThrowingCallable throwingCallable = () -> + centerQueryService.validateCenterExists(nonExistentId); + + // then + assertThatExceptionOfType(BadRequestException.class) + .isThrownBy(throwingCallable) + .withMessage(ExceptionMessage.NOT_EXISTS_CENTER.getMessage()); + } + + @DisplayName("존재하는 기관 ID로 조회 시 예외가 발생하지 않는다") + @Test + void validateExistingCenter() { + // given + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + Center savedCenter = centerRepository.save(center); + + // when + ThrowableAssert.ThrowingCallable callable = () -> centerQueryService.validateCenterExists(savedCenter.getId()); + + // then + assertThatCode(callable).doesNotThrowAnyException(); + } + +} diff --git a/src/test/java/com/somemore/center/service/query/PreferItemQueryServiceTest.java b/src/test/java/com/somemore/center/service/query/PreferItemQueryServiceTest.java new file mode 100644 index 000000000..4f7475f93 --- /dev/null +++ b/src/test/java/com/somemore/center/service/query/PreferItemQueryServiceTest.java @@ -0,0 +1,75 @@ +package com.somemore.center.service.query; + +import com.somemore.center.domain.PreferItem; +import com.somemore.center.dto.response.PreferItemResponseDto; +import com.somemore.center.repository.PreferItemRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class PreferItemQueryServiceTest { + + @Mock + private PreferItemRepository preferItemRepository; + + @InjectMocks + private PreferItemQueryService preferItemQueryService; + + @Test + void getPreferItemDtosByCenterId() { + // Given + UUID centerId = UUID.randomUUID(); + PreferItem preferItem1 = createPreferItem(centerId, "item1"); + PreferItem preferItem2 = createPreferItem(centerId, "item2"); + List preferItems = List.of(preferItem1, preferItem2); + + when(preferItemRepository.findByCenterId(centerId)).thenReturn(preferItems); + + // When + List result = preferItemQueryService.getPreferItemDtosByCenterId(centerId); + + // Then + assertThat(result).hasSize(2); + assertThat(result.get(0).centerId()).isEqualTo(centerId); + assertThat(result.get(0).itemName()).isEqualTo("item1"); + + assertThat(result.get(1).centerId()).isEqualTo(centerId); + assertThat(result.get(1).itemName()).isEqualTo("item2"); + + verify(preferItemRepository, times(1)).findByCenterId(centerId); + } + + @Test + void getPreferItemsByCenterId() { + // Given + UUID centerId = UUID.randomUUID(); + PreferItem preferItem1 = createPreferItem(centerId, "item1"); + PreferItem preferItem2 = createPreferItem(centerId, "item2"); + List expectedItems = List.of(preferItem1, preferItem2); + + when(preferItemRepository.findByCenterId(centerId)).thenReturn(expectedItems); + + // When + List result = preferItemQueryService.getPreferItemsByCenterId(centerId); + + // Then + assertThat(result).hasSize(2).isEqualTo(expectedItems); + verify(preferItemRepository, times(1)).findByCenterId(centerId); + } + + private PreferItem createPreferItem(UUID centerId, String itemName) { + return PreferItem.builder() + .centerId(centerId) + .itemName(itemName) + .build(); + } +} \ No newline at end of file From ffcafa19ece6ce83150b8cd1d6eb68f7eec0b78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:52:57 +0900 Subject: [PATCH 49/49] refactor(ProcessNaverOAuthUserService): @Component -> @Service --- .../naver/service/query/ProcessNaverOAuthUserService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/somemore/auth/oauth/naver/service/query/ProcessNaverOAuthUserService.java b/src/main/java/com/somemore/auth/oauth/naver/service/query/ProcessNaverOAuthUserService.java index df5c864ce..f6d27d6e3 100644 --- a/src/main/java/com/somemore/auth/oauth/naver/service/query/ProcessNaverOAuthUserService.java +++ b/src/main/java/com/somemore/auth/oauth/naver/service/query/ProcessNaverOAuthUserService.java @@ -1,20 +1,20 @@ package com.somemore.auth.oauth.naver.service.query; import com.somemore.auth.oauth.OAuthProvider; +import com.somemore.auth.oauth.naver.dto.response.NaverUserProfileResponseDto; import com.somemore.auth.oauth.naver.usecase.query.CheckNaverUserUseCase; import com.somemore.auth.oauth.usecase.ProcessOAuthUserUseCase; -import com.somemore.auth.oauth.naver.dto.response.NaverUserProfileResponseDto; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import static com.somemore.auth.oauth.naver.util.OAuthResponseConverter.convertToNaverUserProfileResponseDto; @Slf4j -@Component +@Service @RequiredArgsConstructor @Transactional(readOnly = true) public class ProcessNaverOAuthUserService implements ProcessOAuthUserUseCase {