Skip to content

[feat] 자체 회원가입 처리 구현#108

Merged
iamseojin merged 3 commits intowith-travel:developfrom
iamseojin:feat/issue-#107
Aug 29, 2025
Merged

[feat] 자체 회원가입 처리 구현#108
iamseojin merged 3 commits intowith-travel:developfrom
iamseojin:feat/issue-#107

Conversation

@iamseojin
Copy link
Contributor

이슈

변경 요약

  • 소셜 기반 회원가입 → 자체 이메일/비밀번호 회원가입으로 전환
  • 회원가입 시 설문(Survey) 과 회원 추가정보를 동시에 등록
  • 로그인은 이메일 + 비밀번호 조합으로 인증
  • 비밀번호는 BCrypt(PasswordEncoder) 로 해싱 저장 (DB에는 원문 저장 없음)
  • 로그인/가입 완료 시 Access/Refresh 토큰과 추가정보/설문 완료 여부(infoChecked) 반환

변경 사항

  1. 회원가입 (이메일 + 비밀번호 방식)

    • 사용자가 이메일, 비밀번호, 이름, 닉네임, 성별, 생년월일 같은 기본정보랑 설문지를 같이 제출하면,
    • 먼저 같은 이메일이 이미 등록돼 있는지 확인하고,
    • 비밀번호는 그대로 저장하지 않고 "암호화(안전하게 변환)" 해서 DB에 넣어요.
    • 그 다음 회원 정보를 저장하고, 설문도 같이 연결해서 저장합니다.
    • 마지막으로 회원이 추가 정보를 다 채웠다는 표시를 해주고, 바로 로그인할 수 있도록 토큰 2개(엑세스·리프레시)를 돌려줍니다.
  2. 로그인 (이메일 + 비밀번호)

    • 사용자가 이메일과 비밀번호로 로그인하면,
    • DB에 저장된 암호화된 비밀번호와 비교해서 맞는지 확인합니다.
    • 맞으면 토큰 2개(엑세스·리프레시)를 발급해주고, 그 사람이 추가정보/설문까지 끝낸 상태인지 true/false 로 알려줍니다.
  3. 이메일 중복 확인

    • 회원가입 전에 이메일이 이미 쓰이고 있는지 확인할 수 있는 기능을 따로 제공합니다.
    • 결과는 단순히 true/false 로 알려줍니다. (T: 해당 이메일 사용가능/F: 중복 이메일 존재)
  4. 보안 처리

    • 비밀번호는 절대 그대로 저장하지 않고 암호화해서만 DB에 저장됩니다.

변경 근거

  • 외부 소셜 의존 없이 단일(로컬) 로그인 플로우 필요
  • 보안 강화: 비밀번호는 반드시 해시(BCrypt) 로만 저장
  • 프론트에서 추가정보 입력 여부를 바로 구분해야 하므로 infoChecked 제공

@iamseojin iamseojin requested a review from Copilot August 29, 2025 17:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

이 PR은 소셜 로그인에서 자체 이메일/비밀번호 기반 인증 시스템으로 전환하는 기능을 구현합니다. 회원가입 시 설문조사와 회원 정보를 동시에 등록하고, BCrypt를 사용한 비밀번호 해싱 보안을 적용합니다.

  • 자체 이메일/비밀번호 회원가입 및 로그인 시스템 구현
  • 회원가입과 설문조사 통합 처리
  • BCrypt 기반 비밀번호 암호화 적용

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
ErrorCode.java 로그인 관련 에러 코드 추가 (중복 이메일, 잘못된 인증 정보)
SecurityConfig.java 인증 엔드포인트 허용 및 PasswordEncoder 빈 추가
MemberSignupService.java 회원 추가 정보 업데이트 메서드 파라미터 확장
LocalAuthService.java 로컬 인증 서비스 구현 (회원가입, 로그인, 이메일 중복 확인)
LoginResponse.java 로그인 응답 DTO 추가
LocalLoginRequest.java 로그인 요청 DTO 추가
AuthController.java 인증 관련 REST API 엔드포인트 구현
Member.java Member 엔티티에 LOCAL 로그인 타입 추가 및 빌더 패턴 적용
Comments suppressed due to low confidence (1)

src/main/java/com/arom/with_travel/global/config/SecurityConfig.java:1

  • 두 개의 빈이 동일한 BCryptPasswordEncoder 인스턴스를 생성하고 있어 중복됩니다. 하나의 빈만 유지하고 다른 하나는 제거하거나, 기존 bCryptPasswordEncoder() 메서드를 passwordEncoder()로 이름을 변경하는 것을 권장합니다.
package com.arom.with_travel.global.config;

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 89 to 92
Member m = memberRepository.findByEmail(req.getEmail())
.orElseThrow(() -> BaseException.from(ErrorCode.MEMBER_NOT_FOUND));

if (m.getPassword() == null || !passwordEncoder.matches(req.getPassword(), m.getPassword())) {
Copy link

Copilot AI Aug 29, 2025

Choose a reason for hiding this comment

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

비밀번호가 null인 경우와 비밀번호가 일치하지 않는 경우를 동일한 에러 메시지로 처리하는 것은 좋지만, 사용자가 존재하지 않는 경우와 비밀번호가 틀린 경우도 동일한 에러 코드(INVALID_CREDENTIALS)로 처리하여 계정 열거 공격을 방지하는 것을 권장합니다.

Suggested change
Member m = memberRepository.findByEmail(req.getEmail())
.orElseThrow(() -> BaseException.from(ErrorCode.MEMBER_NOT_FOUND));
if (m.getPassword() == null || !passwordEncoder.matches(req.getPassword(), m.getPassword())) {
Member m = memberRepository.findByEmail(req.getEmail()).orElse(null);
if (m == null || m.getPassword() == null || !passwordEncoder.matches(req.getPassword(), m.getPassword())) {

Copilot uses AI. Check for mistakes.
Comment on lines +155 to 165
public void updateExtraInfo(String nickname, LocalDate birth, Gender gender, String introduction,
String email, String password, String name, String phone) {
this.nickname = nickname;
this.birth = birth;
this.gender = gender;
this.introduction = introduction;
this.email = email;
this.password = password;
this.name = name;
this.phone = phone;
}
Copy link

Copilot AI Aug 29, 2025

Choose a reason for hiding this comment

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

updateExtraInfo 메서드가 이제 기본 정보(email, password, name, phone)까지 업데이트하므로 메서드명이 부적절합니다. updateMemberInfo 또는 updateAllInfo와 같이 더 명확한 이름으로 변경하는 것을 권장합니다.

Copilot uses AI. Check for mistakes.
Comment on lines 89 to 92
Member m = memberRepository.findByEmail(req.getEmail())
.orElseThrow(() -> BaseException.from(ErrorCode.MEMBER_NOT_FOUND));

if (m.getPassword() == null || !passwordEncoder.matches(req.getPassword(), m.getPassword())) {
Copy link

Copilot AI Aug 29, 2025

Choose a reason for hiding this comment

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

로그인 실패 시 MEMBER_NOT_FOUND와 INVALID_CREDENTIALS 두 가지 다른 에러 코드를 사용하면 공격자가 유효한 이메일 주소를 식별할 수 있습니다. 모든 로그인 실패 케이스에 대해 동일한 에러 코드를 사용하는 것을 권장합니다.

Suggested change
Member m = memberRepository.findByEmail(req.getEmail())
.orElseThrow(() -> BaseException.from(ErrorCode.MEMBER_NOT_FOUND));
if (m.getPassword() == null || !passwordEncoder.matches(req.getPassword(), m.getPassword())) {
// Use a dummy password hash to prevent timing attacks and user enumeration
final String DUMMY_PASSWORD_HASH = "$2a$10$7EqJtq98hPqEX7fNZaFWoOa5g5r9Z3rro3yd1y0T5k4bFZ5WD7FZm"; // bcrypt for "dummy_password"
Member m = memberRepository.findByEmail(req.getEmail()).orElse(null);
String passwordHash = (m != null && m.getPassword() != null) ? m.getPassword() : DUMMY_PASSWORD_HASH;
if (m == null || !passwordEncoder.matches(req.getPassword(), passwordHash)) {

Copilot uses AI. Check for mistakes.
@iamseojin iamseojin merged commit ae7b0e7 into with-travel:develop Aug 29, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments