<aside> 💡 서울과학기술대학교 GDG on Campus: SeoulTech 4TH Backend 세션 자료입니다.

</aside>

✅ 2주차 🦆

✅ User 리포지터리와 서비스 생성

레포지토리는 생성된 데이터베이스 테이블의 데이터들을 저장, 조회, 수정, 삭제할 수 있도록 도와주는 인터페이스이다. JpaRepository는 JPA가 제공하는 인터페이스 중 하나로 CRUD 작업을 처리하는 메서드들을 이미 내장하고 있다.

CRUD는 Create, Read, Update, Delete의 앞글자만 따 만든 단어로, 데이터 처리의 기본 기능을 의미한다.

@Repository : 스프링이 레포지토리로 인식하게 만들어주는 애너테이션

// user/repository/UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    Optional<User> findByEmail(String email);

    //TODO: 페이징,검색 기능 추가
}

User 서비스에는 User 리포지터리를 사용하여 회원을 생성하는 signup 메서드를 추가했다. 이떄 User의 비밀번호는 보안을 위해 반드시 암호화하여 저장해야 한다. 그래서 PasswordEncoder를 구현하여 암호화하여 비밀번호를 저장했다(바로 다음에 작성 예정)

@RequiredArgsConstructor : final 변수를 모두 포함하는 생성자를 만들어주는 애너테이션

@Transactional : DB와 동시 연결하는 요청을 한개로 제한해주는 애너테이션. 동시성 문제 해결

// user/service/UserService.java
package gdsc.session.user.service;

import gdsc.session.user.dto.UserInfo;
import gdsc.session.util.PasswordEncoder;
import gdsc.session.user.domain.User;
import gdsc.session.user.dto.LoginRequest;
import gdsc.session.user.dto.SignupRequest;
import gdsc.session.user.exception.AlreadyExistsEmailException;
import gdsc.session.user.exception.PasswordNotMatchException;
import gdsc.session.user.exception.UserNotFound;
import gdsc.session.user.repository.UserRepository;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;

    @Transactional
    public void signup(SignupRequest signupRequest) {
        Optional<User> userOptional = userRepository.findByEmail(signupRequest.getEmail());
     

        String encryptedPassword = getEncryptedPassword(signupRequest.getEmail(),signupRequest.getPassword1());

        User user = User.builder()
                .email(signupRequest.getEmail())
                .password(encryptedPassword)
                .username(signupRequest.getUsername())
                .build();

        userRepository.save(user);
    }

    //@Transactional(readOnly = true)
    //public UserInfo login(LoginRequest loginRequest) {
    //    User user = userRepository.findByEmail(loginRequest.getEmail()).get();

    //    return UserInfo.builder()
    //            .id(user.getId())
    //            .email(user.getEmail())
    //            .username(user.getUsername())
    //            .build();
    //}

    private String getEncryptedPassword(String email, String password) {
        return passwordEncoder.encode(email, password);
    }
}

 PasswordEncoder 클래스 생성

앞서 얘기했듯이 비밀번호는 데이터베이스에 암호화되어 저장되어야 한다. 그래서 비밀번호를 암호화하는 클래스를 생성했다.(중요한 내용 아니므로 복붙)

@Component : 스프링 빈으로 생성하는 애너테이션

// utils/PasswordEncoder.java
@Component
public class PasswordEncoder {

    public String encode(String email, String password) {
        try {
            KeySpec spec = new PBEKeySpec(password.toCharArray(), getSalt(email), 85319, 128);
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

            byte[] hash = factory.generateSecret(spec).getEncoded();
            return Base64.getEncoder().encodeToString(hash);
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException |
                 InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }
		
		// 비밀번호가 같은 사용자끼리 암호화한 값이 같으므로 이메일을 이용해 솔트값 생성
		// 사용자별로 암호화된 비밀번호 값이 다르다
    private byte[] getSalt(String email)
            throws NoSuchAlgorithmException, UnsupportedEncodingException {

        MessageDigest digest = MessageDigest.getInstance("SHA-512");
        byte[] keyBytes = email.getBytes("UTF-8");

        return digest.digest(keyBytes);
    }
}

✅ 회원가입 컨트롤러 생성

회원 가입을 위한 엔티티와 서비스가 준비되었으니 URL 폼을 위한 컨트롤러를 만들어보자.

@PostMapping(”/signup”)을 통해 /user/signup 으로 들어오는 POST 요청을 signup 메서드가 처리한다. 빈 검증을 도입하고 서비스 코드를 호출하여 회원가입 로직을 진행한다.

@Slf4j : 로그를 남기는 애너테이션

@RestController : API 통신 방식으로 컨트롤러 작성