Skip to content

Commit

Permalink
Feature/#164 로그인 및 회원가입 api 연결 (#166)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
llddang authored Aug 25, 2024
1 parent 5974799 commit 05aac5c
Show file tree
Hide file tree
Showing 33 changed files with 532 additions and 275 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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("회원의 토큰")
);

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const UserName = () => {

return (
<span style={{ font: FONT_STYLE.xs.normal, color: COLOR.comment, display: 'flex', alignItems: 'center' }}>
반갑습니다! <span style={{ color: COLOR.primary.main }}>{auth.username}</span>
반갑습니다! <span style={{ color: COLOR.primary.main }}>{auth.name}</span>
</span>
);
};
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/adminComponents/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Header = () => (
<HeaderLayout>
<div style={{ display: 'flex' }}>
<LogoLink href="/admin">
<Image src="/images/logo/SW_logo.svg" alt="SW_logo" width="120" height="40" priority={false} />
<Image src="" alt="SW_logo" width="120" height="40" priority={false} />
</LogoLink>
<Navigator />
</div>
Expand Down
82 changes: 57 additions & 25 deletions frontend/src/app/(auth)/sign-in/components/InputUserInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLInputElement>) => {
setUserInfo((prev) => {
return {
...prev,
[e.target.id]: e.target.value,
};
});
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
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 (
<>
<InputWrapper>
<InputLabel htmlFor="id">아이디(이메일)</InputLabel>
<Input id="id" placeholder="아이디 입력" />
<InputFixedText>@pusan.ac.kr</InputFixedText>
</InputWrapper>
<InputWrapper>
<InputLabel htmlFor="password">비밀번호</InputLabel>
<Input id="password" placeholder="비밀번호 입력" />
</InputWrapper>
<SignButton type="button" onClick={handleSignInClick}>
로그인
</SignButton>
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<InputWrapper>
<InputLabel htmlFor="email">아이디(이메일)</InputLabel>
<Input id="email" placeholder="아이디 입력" value={userInfo.email} onChange={handleInputChange} />
<InputFixedText>@pusan.ac.kr</InputFixedText>
</InputWrapper>
<InputWrapper>
<InputLabel htmlFor="password">비밀번호</InputLabel>
<Input
type="password"
id="password"
placeholder="비밀번호 입력"
value={userInfo.password}
onChange={handleInputChange}
/>
</InputWrapper>
<SignButton type="submit">로그인</SignButton>
</form>
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const EmailTextInput = ({ ...props }: TextInputProps) => {
const hasError = errorText !== undefined;

return (
<div className="flex flex-col gap-1">
<div className="flex grow flex-col gap-1">
<label htmlFor={inputProps.id || inputProps.name} className="text-sm font-semibold">
{label} <span className="text-sm font-semibold text-red-400">*</span>{' '}
<span className="text-xs text-comment">
Expand Down
Loading

0 comments on commit 05aac5c

Please sign in to comment.