Java | Spring

[Spring Boot] Bank App (5) 회원 가입 및 로그인 코드

@leem 2025. 2. 18. 11:50

회원 가입 및 로그인 코드 정리

1. DTO(Data Transfer Object) 설계

회원가입과 로그인을 처리하기 위해 DTO를 사용합니다.

회원가입 DTO (SignUpDTO)

 
package com.tenco.bank.dto;

import com.tenco.bank.repository.model.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SignUpDTO {
    private String username;
    private String password;
    private String fullname;

    // DTO -> User 객체 변환
    public User toUser() {
        return User.builder()
                .username(this.username)
                .password(this.password)
                .fullname(this.fullname)
                .build();
    }
}
  • 회원가입 시 username, password, fullname을 입력받음.
  • toUser() 메서드를 통해 User 객체로 변환하여 DB에 저장.

로그인 DTO (SignInDTO)

 
package com.tenco.bank.dto;

import com.tenco.bank.repository.model.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SignInDTO {
    private String username;
    private String password;

    public User toUser() {
        return User.builder()
                .username(this.username)
                .password(this.password)
                .build();
    }
}
  • 로그인 시 username, password를 입력받음.
  • 마찬가지로 toUser() 메서드를 통해 User 객체 변환.

2. 사용자 컨트롤러 (UserController)

회원가입 및 로그인 요청을 처리하는 컨트롤러입니다.

 
package com.tenco.bank.controller;

import com.tenco.bank.dto.SignInDTO;
import com.tenco.bank.dto.SignUpDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.repository.model.User;
import com.tenco.bank.service.UserService;
import com.tenco.bank.utils.Define;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequiredArgsConstructor
@RequestMapping("/user")
public class UserController {

    private final UserService userService;
    private final HttpSession session;

    // 회원가입 페이지
    @GetMapping("/sign-up")
    public String signUpPage() {
        return "/user/signUp";
    }

    // 회원가입 처리
    @PostMapping("/sign-up")
    public String signProc(SignUpDTO dto) {
        if(dto.getUsername() == null || dto.getUsername().isEmpty()) {
            throw new DataDeliveryException("username을 입력하세요", HttpStatus.BAD_REQUEST);
        }
        if(dto.getPassword() == null || dto.getPassword().isEmpty()) {
            throw new DataDeliveryException("password를 입력하세요", HttpStatus.BAD_REQUEST);
        }
        if(dto.getFullname() == null || dto.getFullname().isEmpty()) {
            throw new DataDeliveryException("fullname을 입력하세요", HttpStatus.BAD_REQUEST);
        }

        userService.createUser(dto);
        return "redirect:/user/sign-in";
    }

    // 로그인 페이지
    @GetMapping("/sign-in")
    public String signInPage() {
        return "/user/signIn";
    }

    // 로그인 처리
    @PostMapping("/sign-in")
    public String signProc(SignInDTO dto) {
        if(dto.getUsername() == null || dto.getUsername().isEmpty()) {
            throw new DataDeliveryException(Define.ENTER_YOUR_USERNAME, HttpStatus.BAD_REQUEST);
        }
        if(dto.getPassword() == null || dto.getPassword().isEmpty()) {
            throw new DataDeliveryException(Define.ENTER_YOUR_PASSWORD, HttpStatus.BAD_REQUEST);
        }

        User principal = userService.readUser(dto);
        session.setAttribute(Define.PRINCIPAL, principal);

        return "redirect:/account/list";
    }

    // 로그아웃 처리
    @GetMapping("/logout")
    public String logout() {
        session.invalidate();
        return "redirect:/user/sign-in";
    }
}
  • signProc()에서 유효성 검사 후 UserService를 호출하여 회원가입 및 로그인 수행.
  • 로그인 성공 시 session에 사용자 정보 저장.
  • logout()에서는 session.invalidate()를 호출하여 로그아웃 처리.

3. 사용자 서비스 (UserService)

회원가입과 로그인을 처리하는 서비스 계층입니다.

 
package com.tenco.bank.service;

import com.tenco.bank.dto.SignInDTO;
import com.tenco.bank.dto.SignUpDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.RedirectException;
import com.tenco.bank.repository.interfaces.UserRepository;
import com.tenco.bank.repository.model.User;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;

    @Transactional
    public void createUser(SignUpDTO dto) {
        try {
            int result = userRepository.insert(dto.toUser());
            if (result != 1) {
                throw new DataDeliveryException("회원 가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
            }
        } catch (DataAccessException e) {
            throw new DataDeliveryException("잘못된 처리입니다", HttpStatus.INTERNAL_SERVER_ERROR);
        } catch (Exception e) {
            throw new RedirectException("알 수 없는 오류", HttpStatus.SERVICE_UNAVAILABLE);
        }
    }

    public User readUser(SignInDTO dto) {
        User user = userRepository.findByUsernameAndPassword(dto.getUsername(), dto.getPassword());
        if(user == null) {
            throw new DataDeliveryException("아이디 혹은 비밀번호가 틀렸습니다.", HttpStatus.BAD_REQUEST);
        }
        return user;
    }
}
  • createUser()에서 예외 처리트랜잭션 처리 추가.
  • readUser()에서 로그인 실패 시 DataDeliveryException 발생.

4. 사용자 리포지토리 (UserRepository)

MyBatis를 사용하여 데이터베이스와 연결합니다.

 
package com.tenco.bank.repository.interfaces;

import com.tenco.bank.repository.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserRepository {
    int insert(User user);
    int updateById(User user);
    int deleteById(Integer id);
    List<User> findAll();
    User findByUsernameAndPassword(@Param("username") String username, @Param("password") String password);
}
  • insert(), updateById(), deleteById() 등을 포함한 CRUD 메서드 정의.
  • findByUsernameAndPassword()로 사용자 조회.

5. 회원가입 및 로그인 SQL

 
<mapper namespace="com.tenco.bank.repository.interfaces.UserRepository">

    <insert id="insert">
        insert into user_tb (username, password, fullname, created_at)
        values (#{username}, #{password}, #{fullname}, now())
    </insert>

    <select id="findByUsernameAndPassword" resultType="com.tenco.bank.repository.model.User">
        select * from user_tb where username = #{username} and password = #{password}
    </select>

</mapper>
  • 회원가입 (insert) 쿼리와 로그인 시 사용자 조회 (findByUsernameAndPassword) 쿼리 정의.
DTO를 활용하여 회원가입 및 로그인 데이터를 전달.
UserController에서 요청을 받아 UserService를 통해 처리.
\UserRepository를 통해 MyBatis로 DB와 연동.
session을 활용하여 로그인 상태 관리.
유효성 검사예외 처리를 적용하여 안정적인 회원 관리 기능 구현.