From 05aac5cea498bb7b58b035de213271954f40b7de Mon Sep 17 00:00:00 2001 From: llddang <77055208+llddang@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:50:38 +0900 Subject: [PATCH] =?UTF-8?q?Feature/#164=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=B0=8F=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20api=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=20(#166)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 메일 인증코드 보내기 전에 중복된 이메일인지 확인 #164 * feat: 전화번호는 중복될 가능성이 있으므로 검증 로직에서 제외 #164 * feat: 메일 인증 코드 field 추가 #164 * feat: career 관련 util 추가 #109 * feat: 학번 중복 확인 API 및 회원가입 api 정의 #164 * feat: 백엔드에서 보내는 error.message 참조할 수있도록 수정 #164 * feat: 회원가입 및 학번 검증 api 연결 #164 * feat: 로그인 반환값에 관리자 인지 판별하는 속성 추가 #164 * feat: 로그인 api 정의 및 연결 #164 * fix: redux의 state 명 변경에 따른 참조 파일들에서 변수명 변경 #164 * feat: form submit 후 자동 refresh 기능 없애기 #164 * refactor: 사용하지 않는 import 구문 삭제 #164 * fix: 서버 api error handling #164 * fix: 로그인 test에서 response에 is_moderator 속성 추가 #164 * refactor: main page 에서 발생하는 error 및 waring 조치 #164 --- .../auth/application/AuthEmailService.java | 11 + .../auth/application/AuthSignInService.java | 5 +- .../auth/application/AuthSignUpService.java | 1 - .../dto/response/SignInResponse.java | 5 +- .../restdocs/docs/SignInApiDocsTest.java | 3 +- .../Header/components/UserName/index.tsx | 2 +- frontend/src/adminComponents/Header/index.tsx | 2 +- .../components/InputUserInfo/index.tsx | 82 ++++-- .../components/EmailTextInput/index.tsx | 2 +- .../components/SignUpFirstPage/index.tsx | 258 +++++++++++------- frontend/src/app/(auth)/sign-up/page.tsx | 23 +- frontend/src/app/(auth)/styled.ts | 1 + .../MilestoneHistorySection/index.tsx | 25 +- .../components/MilestoneSection/index.tsx | 2 +- .../components/StudentInfoSection/index.tsx | 8 +- .../components/MilestoneDetail/index.tsx | 2 +- .../MilestoneHistoryTable/index.tsx | 2 +- .../components/MilestoneOverview/index.tsx | 2 +- .../MilestoneHistoryTable/index.tsx | 23 +- .../app/admin/milestone/list/[slug]/page.tsx | 7 +- .../src/app/admin/milestone/list/page.tsx | 13 +- .../src/app/components/ExternalLink/index.tsx | 4 +- frontend/src/app/components/PnuLink/index.tsx | 4 +- .../SignIn/components/InputUserInfo/index.tsx | 67 +++-- frontend/src/components/Header/index.tsx | 16 +- frontend/src/data/queryKey.ts | 1 + frontend/src/data/signUp.ts | 10 +- frontend/src/lib/api/server.api.ts | 81 ++++-- frontend/src/lib/api/server.axios.ts | 4 +- frontend/src/lib/hooks/useApi.ts | 48 +++- frontend/src/lib/utils/utils.tsx | 19 +- frontend/src/store/auth.slice.ts | 16 +- frontend/src/types/error.ts | 58 ++-- 33 files changed, 532 insertions(+), 275 deletions(-) diff --git a/backend/src/main/java/sw_css/auth/application/AuthEmailService.java b/backend/src/main/java/sw_css/auth/application/AuthEmailService.java index 6f0d87b9..336daf02 100644 --- a/backend/src/main/java/sw_css/auth/application/AuthEmailService.java +++ b/backend/src/main/java/sw_css/auth/application/AuthEmailService.java @@ -8,6 +8,8 @@ import sw_css.auth.application.dto.response.SendAuthCodeResponse; import sw_css.auth.domain.EmailAuthRedis; import sw_css.auth.domain.repository.EmailAuthRedisRepository; +import sw_css.auth.exception.AuthException; +import sw_css.auth.exception.AuthExceptionType; import sw_css.utils.MailUtil; @Service @@ -20,8 +22,11 @@ public class AuthEmailService { private final MailUtil mailUtil; private final EmailAuthRedisRepository emailAuthRedisRepository; + private final AuthCheckDuplicateService authCheckDuplicateService; public SendAuthCodeResponse emailAuth(String email) { + checkIsDuplicateEmail(email); + String authCode = generateRandomAuthCode(); emailAuthRedisRepository.save(EmailAuthRedis.of(email, authCode)); sendAuthCode(email, authCode); @@ -52,4 +57,10 @@ private void sendAuthCode(String email, String authCode) { String text = "SW역량강화플랫폼 인증코드는 " + authCode + " 입니다."; mailUtil.sendMail(toUserList, subject, text); } + + private void checkIsDuplicateEmail(String email) { + if (authCheckDuplicateService.isDuplicateEmail(email)) { + throw new AuthException(AuthExceptionType.MEMBER_EMAIL_DUPLICATE); + } + } } diff --git a/backend/src/main/java/sw_css/auth/application/AuthSignInService.java b/backend/src/main/java/sw_css/auth/application/AuthSignInService.java index 36ebb577..11c082e0 100644 --- a/backend/src/main/java/sw_css/auth/application/AuthSignInService.java +++ b/backend/src/main/java/sw_css/auth/application/AuthSignInService.java @@ -39,10 +39,11 @@ public SignInResponse signIn(String email, String rawPassword) { } String role = loadMemberRole(member); - + boolean isModerator = role.equals(Role.ROLE_ADMIN.toString()); + String accessToken = jwtTokenProvider.createToken(member.getId(), role); - return SignInResponse.of(member, role, accessToken); + return SignInResponse.of(member, role, isModerator, accessToken); } public void resetPassword(String email, String name) { diff --git a/backend/src/main/java/sw_css/auth/application/AuthSignUpService.java b/backend/src/main/java/sw_css/auth/application/AuthSignUpService.java index 5b2a74e9..45a4e57f 100644 --- a/backend/src/main/java/sw_css/auth/application/AuthSignUpService.java +++ b/backend/src/main/java/sw_css/auth/application/AuthSignUpService.java @@ -29,7 +29,6 @@ public class AuthSignUpService { public long signUp(SignUpRequest request) { checkIsDuplicateEmail(request.email()); checkIsDuplicateStudentId(request.student_id()); - checkIsDuplicatePhoneNumber(request.phone_number()); String actualAuthCode = loadActualAuthCode(request.email()); checkAuthCodeMatch(request.auth_code(), actualAuthCode); diff --git a/backend/src/main/java/sw_css/auth/application/dto/response/SignInResponse.java b/backend/src/main/java/sw_css/auth/application/dto/response/SignInResponse.java index 3c28892d..19519d72 100644 --- a/backend/src/main/java/sw_css/auth/application/dto/response/SignInResponse.java +++ b/backend/src/main/java/sw_css/auth/application/dto/response/SignInResponse.java @@ -7,10 +7,11 @@ public record SignInResponse( String name, String email, String role, + boolean is_moderator, String token ) { - public static SignInResponse of(Member member, String role, String token) { - return new SignInResponse(member.getId(), member.getName(), member.getEmail(), role, token); + public static SignInResponse of(Member member, String role, Boolean isModerator, String token) { + return new SignInResponse(member.getId(), member.getName(), member.getEmail(), role, isModerator, token); } } diff --git a/backend/src/test/java/sw_css/restdocs/docs/SignInApiDocsTest.java b/backend/src/test/java/sw_css/restdocs/docs/SignInApiDocsTest.java index 59065853..c4fc1f30 100644 --- a/backend/src/test/java/sw_css/restdocs/docs/SignInApiDocsTest.java +++ b/backend/src/test/java/sw_css/restdocs/docs/SignInApiDocsTest.java @@ -40,6 +40,7 @@ public void signIn() throws Exception { fieldWithPath("email").type(JsonFieldType.STRING).description("부산대학교 이메일"), fieldWithPath("name").type(JsonFieldType.STRING).description("실명"), fieldWithPath("role").type(JsonFieldType.STRING).description("회원의 역할 (ADMIN, MEMBER)"), + fieldWithPath("is_moderator").type(JsonFieldType.BOOLEAN).description("관리자 인지 판별"), fieldWithPath("token").type(JsonFieldType.STRING).description("회원의 토큰") ); @@ -51,7 +52,7 @@ public void signIn() throws Exception { final String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwicm9sZSI6IlJPTEVfTUVNQkVSIiwiaWF0IjoxNzI0MjIxMDI0LCJleHAiOjE3MjQyNTcwMjR9.i1zj-_jRjkdO89_5ixKVgZXWr1V8e0PMr-958YGQAQQ"; final SignInRequest request = new SignInRequest(email, password); - final SignInResponse response = new SignInResponse(member_id, email, name, role, token); + final SignInResponse response = new SignInResponse(member_id, email, name, role, false, token); // when when(authSignInService.signIn(email, password)).thenReturn(response); diff --git a/frontend/src/adminComponents/Header/components/UserName/index.tsx b/frontend/src/adminComponents/Header/components/UserName/index.tsx index 6002b9e3..0897c0a1 100644 --- a/frontend/src/adminComponents/Header/components/UserName/index.tsx +++ b/frontend/src/adminComponents/Header/components/UserName/index.tsx @@ -8,7 +8,7 @@ const UserName = () => { return ( - 반갑습니다! {auth.username}님 + 반갑습니다! {auth.name} ); }; diff --git a/frontend/src/adminComponents/Header/index.tsx b/frontend/src/adminComponents/Header/index.tsx index a693aa0d..9b466a2e 100644 --- a/frontend/src/adminComponents/Header/index.tsx +++ b/frontend/src/adminComponents/Header/index.tsx @@ -12,7 +12,7 @@ const Header = () => (
- SW_logo + SW_logo
diff --git a/frontend/src/app/(auth)/sign-in/components/InputUserInfo/index.tsx b/frontend/src/app/(auth)/sign-in/components/InputUserInfo/index.tsx index 9514efb6..ee785340 100644 --- a/frontend/src/app/(auth)/sign-in/components/InputUserInfo/index.tsx +++ b/frontend/src/app/(auth)/sign-in/components/InputUserInfo/index.tsx @@ -5,43 +5,75 @@ import { useRouter } from 'next/navigation'; import { InputFixedText, Input, InputLabel, InputWrapper, SignButton } from '@/app/(auth)/styled'; import { useAppDispatch, useAppSelector } from '@/lib/hooks/redux'; import { signIn } from '@/store/auth.slice'; +import { useSignInMutation } from '@/lib/hooks/useApi'; +import { useState } from 'react'; +import { toast } from 'react-toastify'; const InputUserInfo = () => { + const [userInfo, setUserInfo] = useState({ + email: '', + password: '', + }); + const router = useRouter(); + + const { mutate: signInMutation } = useSignInMutation(); + const dispatch = useAppDispatch(); const auth = useAppSelector((state) => state.auth).value; if (auth.isAuth) router.push('/'); - const handleSignInClick = () => { - // TODO: api 연결 - dispatch( - signIn({ - token: 'token', - username: 'name', - uid: 202055558, - isModerator: true, - }), - ); - setTimeout(() => { - router.refresh(); - }, 0); + const handleInputChange = (e: React.ChangeEvent) => { + setUserInfo((prev) => { + return { + ...prev, + [e.target.id]: e.target.value, + }; + }); + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + signInMutation(userInfo, { + onSuccess(data, variables, context) { + dispatch( + signIn({ + id: data.member_id, + token: `Bearer ${data.token}`, + name: data.name, + email: data.email, + isModerator: data.is_moderator, + }), + ); + router.push('/'); + }, + onError(error, variables, context) { + toast.error(error.message); + }, + }); }; return ( <> - - 아이디(이메일) - - @pusan.ac.kr - - - 비밀번호 - - - - 로그인 - +
+ + 아이디(이메일) + + @pusan.ac.kr + + + 비밀번호 + + + 로그인 +
); }; diff --git a/frontend/src/app/(auth)/sign-up/components/SignUpFirstPage/components/EmailTextInput/index.tsx b/frontend/src/app/(auth)/sign-up/components/SignUpFirstPage/components/EmailTextInput/index.tsx index c3087b50..e4ca465e 100644 --- a/frontend/src/app/(auth)/sign-up/components/SignUpFirstPage/components/EmailTextInput/index.tsx +++ b/frontend/src/app/(auth)/sign-up/components/SignUpFirstPage/components/EmailTextInput/index.tsx @@ -15,7 +15,7 @@ export const EmailTextInput = ({ ...props }: TextInputProps) => { const hasError = errorText !== undefined; return ( -
+