[인스타그램 클론코딩] 02. 회원 가입 구현(Back-End)

2023. 1. 31. 16:15·Web/인스타 클론 코딩

가장 먼저 회원가입을 구현하였다.

먼저 Spring Boot의 폴더 구조는 다음과 같다.

java.com.woo.outstagram
-- config
-- controller
-- dto
-- entity
-- repository
-- service
-- util

 

1. config : Web관련 설정이나, Spring Security 설정 등을 정의할 패키지이다.

2. controller : 요청 URL에 해당하는 처리를 담당하는 Controller 들을 정의할 패키지이다.

3. dto : 클래스들 사이에서 데이터 전달을 담당하는 DTO 객체들을 정의할 패키지이다.

4. entity : 데이터베이스에 저장될 필드와 여러 연관관계를 정의할 패키지이다.

5. repository : JPA를 사용하기 위한 Repository 객체들을 정의할 패키지이다.

6. service : 비즈니스 로직을 정의할 패키지이다.

7. util : 프로젝트에서 쓰일 여러 기능의 객체들을 정의할 패키지이다.

 

1.  User Entity,  UserRepository 작성

- User 테이블에 해당하는 Entity 객체를 작성한다.

@Entity
@Data
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long id;

    @Column(unique = true)
    private String email;

    private String password;
    private String name;

    private String nickname;
    private String phone;

    @Column(name = "profile_img_url")
    private String profileImgUrl;

    private String introduce;

    private String gender;

    @Enumerated(EnumType.STRING)
    private Role role;

    @Builder
    public User(String email, String password, String name, String nickname, String phone, String profileImgUrl, String introduce, String gender, Role role) {
        this.email = email;
        this.password = password;
        this.name = name;
        this.nickname = nickname;
        this.phone = phone;
        this.profileImgUrl = profileImgUrl;
        this.introduce = introduce;
        this.gender = gender;
        this.role = role;
    }
}

public enum Role {
    ROLE_USER("ROLE_USER"),
    ROLE_ADMIN("ROLE_ADMIN");

    private final String role;
    Role(String role) {
        this.role = role;
    }

    public String getRole() {
        return role;
    }
}

 

- User 테이블의 고유 ID는 Long 타입으로 설정하였으며, 생성 규칙은 GenerationType.IDENTITY 옵션을 통해 MYSQL DB에서 처리하도록 하였다.

- 권한을 저장하기 위해 Enum 타입의 Role 객체를 가져와 String 형태로 저장하도록 하였다.

 

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
    Boolean existsByEmail(String email);
}

 

- Jpa 기능을 사용하기 위하여 JpaRepository 객체를 상속받았으며, 기본적으로 save, delete 등의 메서드는 구현하지 않아도 사용할 수 있지만, 앞으로 필요에 따라 사용하게 될 findByEmail, existByEmail 메서드는 미리 작성해두었다.

 

2. SignUpReauestDto 작성

- DTO는 데이터 전달이 목적인 객체이다.

- Front에서 회원가입 요청이 데이터와 함께 들어오게 되는데, 그 데이터를 비즈니스로직까지 전달해줄 객체이다.

@Data
public class SignUpRequestDto {

    @Email(message = "이메일 양식이 올바르지 않습니다.")
    @NotBlank(message = "빈 항목이 존재합니다.")
    private String email;

    @NotBlank(message = "빈 항목이 존재합니다.")
    private String password;

    @Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "핸드폰 번호의 양식이 올바르지 않습니다. 01x-xxx(x)-xxxx")
    @NotBlank(message = "빈 항목이 존재합니다.")
    private String phone;

    @NotBlank(message = "빈 항목이 존재합니다.")
    private String name;

    @NotBlank(message = "빈 항목이 존재합니다.")
    private String nickname;

    @Builder
    public SignUpRequestDto(String email, String password, String name, String nickname, String phone) {
        this.email = email;
        this.password = password;
        this.name = name;
        this.nickname = nickname;
        this.phone = phone;
    }

    public User toEntity(String password) {
        return User.builder()
                .email(this.email)
                .password(password)
                .name(this.name)
                .nickname(this.nickname)
                .phone(this.phone)
                .profileImgUrl("/static/profileImage/default_profile.png")
                .role(Role.ROLE_USER)
                .gender("")
                .introduce("")
                .build();
    }
}

 

- JSR-303 기능을 통하여 들어오는 데이터 형식을 검증한다. @Email 은 이메일 형식인지, @NotBlank는 null 및 공백인지, @Pattern은 사용자 지정 형식인지를 검증할 수 있게 해준다.

- toEntity 메서드는 DTO 객체를 User 객체로 변환하게 해주는 메서드이다. User 객체 필드 중 DTO객체 필드에 있는 것들은 this 메서드를 사용하여 주입하였고, 나머지는 생성 시 일괄적으로 적용될 초기화 값들을 넣어주었다.

- toEntity 메서드에서 password 파라미터를 받는 이유는, Spring Security에서 제공하는 BCryptPasswordEncoder를 사용하여 비밀번호를 암호화 한 뒤 User 객체에 주입하기 위해서이다.

 

3. Spring Security 설정

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().disable()
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/users/**").permitAll();
    }
}

 

- BCryptPasswordEncoder를 사용하기 위해 passwordEncoder 이름으로 Bean을 등록해준다.

- cors, csrf 설정을 꺼준 뒤, 회원 가입/로그인 등의 요청은 /users 로 시작되는 URL로 들어올 예정이므로 그것에 대하여 권한을 permitAll로 설정해주었다.

 

4. UserService 회원 가입 로직 구현

@Service
@Slf4j
@RequiredArgsConstructor
public class UserService {

    private final PasswordEncoder passwordEncoder;
    private final UserRepository userRepository;

    @Transactional
    public void join(SignUpRequestDto requestDto) throws Exception {
        if(!userRepository.existsByEmail(requestDto.getEmail())) {
            userRepository.save(requestDto.toEntity(passwordEncoder.encode(requestDto.getPassword())));
        } else {
            throw new Exception("이미 가입된 이메일 입니다.");
        }
    }
}

 

- @RequiredArgsConstructor 를 통하여 사용할 PasswordEncoder, UserRepository 객체를 주입받는다.

- 만약 테이블에 requestDto의 이메일과 같은 데이터가 없다면 UserRepository의 save 메서드를 사용하여 DB에 저장하고, 중복된 이메일이 존재한다면 예외를 발생시킨다.

- 앞서 작성한 SignUpRequestDto의 toEntity 메서드의 파라미터에 PasswordEncoder의 encode 메서드를 사용하여 password를 변환한 뒤 전달해 주었다.

 

5. UserController 회원가입 작성

@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    @PostMapping("/join")
    public ResponseEntity join(@Valid @RequestBody SignUpRequestDto requestDto, BindingResult bindingResult) {
        if(bindingResult.hasErrors()) {
            String errorMsg = bindingResult.getFieldError().getDefaultMessage();

            return ResponseEntity.badRequest().body(errorMsg);
        }

        try {
            userService.join(requestDto);

            return ResponseEntity.ok().body("회원가입이 정상적으로 진행되었습니다.");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }
}

 

- Post 메서드의 "/api/users/join" URL 요청은 join이라는 메서든에 지정된 형식으로 처리된다.

- @Valid 어노테이션을 통해 JSR-303검증을, @RequestBody 메서드를 통해 POST의 Body에 넘어온 데이터를 SignUpRequestDto 객체에 매핑시켜준다.

- 만약 JSR-303 검증에서 에러가 발생할 경우, DTO객체에서 지정한 메서드와 함께 400 BadRequest를 반환한다.

- userService의 join 메서드를 실행 후 성공적으로 로직이 수행되었다면 200 OK를, 예외가 발생했다면 400 BadRequest를 반환한다.

저작자표시 비영리 변경금지 (새창열림)

'Web > 인스타 클론 코딩' 카테고리의 다른 글

[인스타그램 클론코딩] 04. JWT 필터 구현(Back-End)  (0) 2023.02.01
[인스타그램 클론코딩] 03. 로그인 구현(Front-End)  (0) 2023.02.01
[인스타그램 클론코딩] 03. 로그인 구현(Back-End)  (0) 2023.02.01
[인스타그램 클론코딩] 02. 회원 가입 구현(Front-End)  (0) 2023.01.31
[인스타그램 클론코딩] 01. 기술 스택 정하기  (0) 2023.01.31
'Web/인스타 클론 코딩' 카테고리의 다른 글
  • [인스타그램 클론코딩] 03. 로그인 구현(Front-End)
  • [인스타그램 클론코딩] 03. 로그인 구현(Back-End)
  • [인스타그램 클론코딩] 02. 회원 가입 구현(Front-End)
  • [인스타그램 클론코딩] 01. 기술 스택 정하기
뚝딱뚝딱2
뚝딱뚝딱2
  • 뚝딱뚝딱2
    개발도상국
    뚝딱뚝딱2
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 공부
        • Java
        • Spring Boot
        • LORA
      • Web
        • 인스타 클론 코딩
        • GPT 응답 API 서버
        • Spring Boot 예외 처리
        • 코테 준비용 서비스 만들기
      • DevOps
        • 쿠버네티스
        • 서버 만들기
      • 코딩테스트
        • 알고리즘
      • 교육
        • 스파르타코딩클럽 - 내일배움단
        • 혼자 공부하는 컴퓨터 구조 운영체제
      • 잡다한것
  • 블로그 메뉴

    • 홈
  • 링크

    • GITHUB
  • 공지사항

  • 인기 글

  • 태그

    인스타그램
    리액트
    스프링부트
    클론코딩
    chat GPT
    OpenAI API
    클러스터
    Entity
    오블완
    Java
    REST API
    예외
    쿠버네티스
    백준
    티스토리챌린지
    스프링 부트
    mapstruct
    react
    spring boot
    MSA
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
뚝딱뚝딱2
[인스타그램 클론코딩] 02. 회원 가입 구현(Back-End)
상단으로

티스토리툴바