회원가입화면 만들기

2024. 12. 17. 22:47·토이 프로젝트 2

이번 프로젝트의 목표는 Spring의 기본 CRUD 감을 익히는 것이 목표이다. 때문에 Spring Security를 사용해볼 수도있겠지만 단계적으로 나아가야한다고 생각하여 가장 기본적인 것부터 실습.

 

추후 목표.

1. JavaScript를 활용하여 유효성 검사

2. Model이 아닌 JSON형식으로 구현해볼것(@RestController 사용)

3. 타임리프대신 리액트를 활용하여 구현.

 

현재 해야할 것이 많지만 너무 조급해하지말고 천천히 하자.

 

1.MemberController

package com.example.bravobra.controller;


import com.example.bravobra.dto.MemberDto;
import com.example.bravobra.dto.request.FindPasswordDto;
import com.example.bravobra.service.MemberService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Controller
@RequiredArgsConstructor
@RequestMapping("/members")
@Slf4j
public class MemberController {

    private final MemberService memberService;


    @GetMapping("/join")
    public String createForm(Model model) {
        // 1. model에 MemberDto객체를 생성해서 보냄.(빈객체)
        model.addAttribute("memberDto", MemberDto.builder().build());
        return "members/createMemberForm"; // 2. createMemberForm을 화면에 보여줌.
    }

    @PostMapping("/join")
    public String create(@Valid MemberDto memberDto, BindingResult result) { // @Valid로 Dto에 할당한 값의 유효성 검사.
        // result에 오류 정보를 저장. // hasErrors() - 바인딩과정에서 오류가 발생했는지 확인.

        // 1. 오류가 발생했으면 다시 회원가입화면으로 이동.
        if (result.hasErrors()) {
            return "members/createMemberForm";
        }

        //2. 오류가 발생하지 않았으면 이메일 중복체크(Service단 처리)
        memberService.join(memberDto);
        return "redirect:/";
    }

    @GetMapping("/list") // dto로 받는 것이 좋음. entity에 직접 접근하는 것은 좋지 않음.
    public String list(Model model) {
        List<MemberDto> members = memberService.findAllMemberDto();
        model.addAttribute("members", members); // 담은 다음 Form으로 전달.
        return "members/MemberListForm";
    }
}

 

 

2. Member(Entity)

package com.example.bravobra.domain;


import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Entity
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String email;

    private String password;

    private String phoneNumber;

    private String nickName;

    @CreationTimestamp()
    private LocalDateTime registerDate; // 계정 생성일시

    private LocalDateTime lastLoginDate; // 마지막 로그인 일시

    @Enumerated(EnumType.STRING) // 문자열로 저장.
    private MemberType memberType; // 사용자 유형


    // 비밀번호 찾거나 변경할 때 사용. setter가 없어서 메서드로.
    public void updatePassword(String newPassword) {
        this.password = newPassword;
    }
}

 

3.MemberDto

package com.example.bravobra.dto;


import com.example.bravobra.domain.MemberType;
import com.example.bravobra.validation.annotation.PasswordMatch;
import com.example.bravobra.validation.annotation.UniqueNickname;
import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@Builder
@AllArgsConstructor
@PasswordMatch
public class MemberDto {

    @NotEmpty(message = "이메일 입력은 필수 입니다")
    @Email(message = "올바른 이메일 형식이 아닙니다.")
    private String email;

    @NotEmpty(message = "이름 입력은 필수 입니다.")
    private String name;


    @NotBlank(message = "비밀번호는 필수 입력 사항입니다.")
    @Size(min = 8, max = 25, message = "비밀번호는 8 ~ 25자입니다.")
    @Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[@#$%^&+=!])(?=\\S+$).{8,}", message = "비밀번호는 최소 하나의 숫자, 하나의 영문자, 하나의 특수문자를 포함해야 합니다.")
    private String password;

    @NotBlank(message = "비밀번호 확인은 필수입니다.")
    private String passwordConfirm;

    // 비밀번호 일치 여부 체크 (어노테이션을 활용한 방법)
    public boolean isPasswordMatch() {
        return password != null && password.equals(passwordConfirm);
    }

    @NotBlank(message = "닉네임 입력은 필수 입력입니다.")
    @UniqueNickname // 내가 만든 어노테이션.
    private String nickname;

    @Pattern(regexp = "^(\\d{2,3})-(\\d{3,4})-(\\d{4})$",message = "000-0000-0000 양식을 맞춰주세용")
    private String phoneNumber;

    private LocalDateTime registerDate;

    private MemberType memberType;
}

 

4.MemberRepository

package com.example.bravobra.repository;


import com.example.bravobra.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {

    Optional<Member> findByEmail(String email);

    boolean existsByEmail(String email); //쿼리 메서드

    List<Member> findAllByEmail(String email); // 조회할때 필요.

    Optional<Member> findByPhoneNumberAndName(String phoneNumber, String name);

    boolean existsByPhoneNumber(String phoneNumber); // 휴대폰 번호 중복 체크

    boolean existsByNickName(String nickName);

    Optional<Member> findByEmailAndNameAndPhoneNumber(String email, String name, String phoneNumber);

    @Modifying
    @Query("UPDATE Member m SET m.password = :password WHERE m.email = :email")
    void updatePassword(@Param("email") String email, @Param("password") String password);


}

 

5.MemberService

package com.example.bravobra.service;


import com.example.bravobra.domain.Member;
import com.example.bravobra.domain.MemberType;
import com.example.bravobra.dto.MemberDto;
import com.example.bravobra.exception.DuplicateMemberException;
import com.example.bravobra.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class MemberService {

    private final MemberRepository memberRepository;
    private final BCryptPasswordEncoder passwordEncoder; // BCryptPasswordEncoder 추가



    // 회원 생성
    @Transactional
    public Long join(MemberDto memberDto) { // Controller에서 memberDto객체를 전달 받음.
        // 1. dto의 이메일 중복체크 메서드.
        validateDuplicateMember(memberDto.getEmail(), memberDto.getPhoneNumber());

        // 비밀번호 암호화
        String encryptedPassword = passwordEncoder.encode(memberDto.getPassword());


        // 2. 중복이 아니면 member에 정보 저장.
        Member member = Member.builder()
                .email(memberDto.getEmail())
                .name(memberDto.getName())
                .password(encryptedPassword)
                .nickName(memberDto.getNickname())
                .phoneNumber(memberDto.getPhoneNumber())
                .memberType(MemberType.MEMBER)
                .build();
        // 확인용
        System.out.println("member = " + member);

        // 3. 리포지터리에 저장.
        memberRepository.save(member);
        return member.getId(); // member id 리턴.( id를 왜 리턴한다고 적었지?)

    }

    //중복체크 이메일
    private void validateDuplicateMember(String email, String phoneNumber) {
        if(memberRepository.existsByEmail(email)){
            throw new DuplicateMemberException("이미 등록된 이메일입니다"); // 이미등록된 이메일일때 예외처리 해주어야함.
        }
        if(memberRepository.existsByPhoneNumber(phoneNumber)){
            throw new DuplicateMemberException("이미 등록된 휴대폰 번호입니다.");
        }
    }

    public List<Member> findAllMember() {
        return memberRepository.findAll();
    }
    // 검색 기능 할 떄 필요 이름 한명 정학 때.
    public Member findOne(String email) {
        return memberRepository.findByEmail(email).orElseThrow(() -> new IllegalStateException("해당 이메일을 가진 회원이 존재하지 않습니다."));
    }

    public List<MemberDto> findAllMemberDto(){
        List<Member> members = memberRepository.findAll();

        return members.stream()
                .map(member -> MemberDto.builder()
                        .email(member.getEmail())
                        .name(member.getName())
                        .password(member.getPassword())
                        .nickname(member.getNickName())
                        .phoneNumber(member.getPhoneNumber())
                        .registerDate(member.getRegisterDate())
                        .memberType(member.getMemberType())
                        .build())
                .collect(Collectors.toList());

    }




}

'토이 프로젝트 2' 카테고리의 다른 글

로그인 횟수 제한 구현  (0) 2024.12.28
비밀번호 찾기 구현  (1) 2024.12.27
Interceptor(권한) 구현  (1) 2024.12.26
커스텀 어노테이션 추가, ExceptionHandler 추가  (0) 2024.12.25
회원가입시 비밀번호 암호화(bCrypto)  (0) 2024.12.24
'토이 프로젝트 2' 카테고리의 다른 글
  • 비밀번호 찾기 구현
  • Interceptor(권한) 구현
  • 커스텀 어노테이션 추가, ExceptionHandler 추가
  • 회원가입시 비밀번호 암호화(bCrypto)
신댕인생
신댕인생
안녕하세요!! 예비개발자 신댕입니다. 개발자를 준비하고 있는 신댕인생의 하루하루를 정리하고 있어요. 파이팅!
  • 신댕인생
    신댕인생
    신댕인생
  • 전체
    오늘
    어제
    • 분류 전체보기 (127)
      • 다시 준비 (0)
        • 계획(ISTJ) (0)
        • 정보처리기사 (0)
        • SQLD (0)
        • Toeic Speaking (0)
        • 일일 회고 (0)
      • 데브캠프 일지 (52)
        • 예제 (6)
        • 연습문제 (12)
        • 과제 (2)
        • 칠판 정리 (1)
        • 숙지용 필기 (11)
      • 책 (12)
        • Clean Architecture (2)
        • 개발자도 궁금한 IT 인프라 (1)
        • JVM 밑바닥까지 파헤치기 (9)
      • DB (23)
        • 숙지용 필기 (12)
        • SQL 쿼리 실습 (7)
        • 극장 시스템 모델링 (3)
      • 토이 프로젝트 2 (7)
      • 토이 프로젝트 3 (0)
      • 뚜띠 (3)
      • 회고 (0)
        • 주간 회고 (0)
        • 월간 회고 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    JPA
    연습문제
    패스트캠퍼스 데브캠프
    패스트 캠퍼스
    데브캠프
    머신러닝 교과서
    sql의 정석
    jvm
    쿼리 실습
    부트캠프
    패스트캠퍼스
    ERD
    백엔드 부트캠프
    자바의 정석
    남궁성
    패스트캠퍼스 부트캠프
    SQL
    spring
    지네릭스
    spring의 정석
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
신댕인생
회원가입화면 만들기
상단으로

티스토리툴바