12월 8일 금 TIL

장숭혁·2023년 12월 10일
0

TIL작성

목록 보기
28/60
@DisplayName("프로필 테스트")
public class ProfileServiceTest {

    private UserRepository userRepository;
    private ProfileService profileService;
    private PasswordCheckingRepository passwordCheckingRepository;

    @BeforeEach
    void setUp() {
        userRepository = mock(UserRepository.class);
        passwordCheckingRepository = mock(PasswordCheckingRepository.class);
        profileService = new ProfileService(userRepository, passwordCheckingRepository);
    }

    @Test
    @Transactional
    @DisplayName("비밀번호 수정 테스트 성공")
    void testUpdateUserPassword(){
        // Given
        Long userId = 1L;
        String username = "testus12";
        String email = "test@example.com";
        String password = "PassWo12";
        String intro = "Test intro";

        var mockUser = User.builder()
                .username(username)
                .password(password)
                .email(email)
                .role(USER)
                .intro(intro)
                .build();
        PasswordChecking passwordChecking = new PasswordChecking(mockUser.getPassword(),mockUser);

        when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));
        when(passwordCheckingRepository.findByUser(mockUser)).thenReturn(passwordChecking);

        // When
        String newPassword1 = "PAssword45";
        String newPassword2 = "PAsSword85";
        String newPassword3 = "PAsSword81";
        String newPassword4 = "PAsSword82";

        profileService.updateUserPassword(userId, newPassword1);
        profileService.updateUserPassword(userId, newPassword2);
        profileService.updateUserPassword(userId, newPassword3);
        profileService.updateUserPassword(userId, newPassword4);

        // Then
        assertThat(mockUser.getPassword()).isEqualTo(newPassword4);
    }

}
  • 비밀번호 변경이력 3회 안으로 같은 비밀번호를 입력했을때 변경 실패를 하는 테스트 중 일단 4번의 비밀번호 변경이 다 다른 경우 비밀번호 변경 성공하는 단위 테스트이다.

  • userRepository = mock(UserRepository.class);: Mockito를 사용하여 UserRepository의 mock 객체를 생성한다. Mock 객체는 실제 구현이 아닌, 가짜 객체로써 테스트에서 사용된다.

  • @Transactional: 해당 테스트 메소드는 트랜잭션을 사용하며, 실행 후 롤백되어야 함을 나타낸다. 테스트에서 DB 상태 변경을 테스트한 후에 롤백하여 테스트 간 영향을 최소화한다. @Test 어노테이션과 같이쓰일 경우.

  • when(userRepository.findById(userId)).thenReturn(Optional.of(mockUser));: Mockito를 사용하여 userRepository의 findById 메소드가 호출될 때, 지정한 userId에 해당하는 mock 객체 mockUser를 반환하도록 설정한다.

* 로그인 유저의 상태 관리 서비스를 제공합니다.
 * 1. JWT 기반 로그인 유저 정보 조회
 * 2. 이메일 인증 코드 관리 서비스
 */
@Component
public class UserStatusService {

    private final ConcurrentMap<String, String> redis = new ConcurrentHashMap<>();

    /**
     * JwtAuthorizationFilter에서 인가된 jwt 토큰 정보를 SecurityContext에서 조회한다.
     *
     * @return jwt 토큰에 담긴 유저 정보 dto를 반환
     */
    public JwtUser getLoginUser() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth.getPrincipal() instanceof JwtUser) return (JwtUser) auth.getPrincipal();
        throw new AccessDeniedException("권한이 없습니다.");
    }

    public void saveEmailAuthCode(String email, String code) {
        redis.put(email, code);
    }

    public void removeEmailAuthCode(String email){
        redis.remove(email);
    }

    public boolean matchesEmailAuthCode(String email, String code) {
        if (!redis.containsKey(email)) return false;
        return redis.get(email).equals(code);
    }
}
  • @Component: 해당 클래스가 Spring 컴포넌트임을 나타내며, 컴포넌트 스캐닝 중 자동으로 감지된다. 이것은 이 클래스를 Spring 빈으로 자동 등록한다.

    (전반적인 프로그래밍 맥락에서)컴포넌트 : 각 부분을 개별적으로 개발, 관리, 배포할 수 있는 재사용 가능한 모듈 단위를 가리킨다.

    • private final ConcurrentMap<String, String> redis = new ConcurrentHashMap<>();: ConcurrentHashMap을 사용하여 이메일 확인 코드를 관리하는 인메모리 스토리지를 초기화한다. 이 맵은 이메일 주소를 키로 하고 해당하는 확인 코드를 값으로 저장한다.

      ConcurrentMap은 Java에서 동시성을 지원하는 맵 인터페이스이다. 여러 스레드가 동시에 맵을 읽고 쓸 수 있도록 설계되었다. 기존의 HashMap과는 달리 ConcurrentMap은 동시성 문제를 해결하기 위해 설계되었다. ConcurrentMap 인터페이스는 다수의 구현체를 가지고 있는데 Java에서 가장 널리 사용되는 구현체 중 하나는 ConcurrentHashMap이다.

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ConcurrentMapExample {
    public static void main(String[] args) {
        // ConcurrentMap 생성
        ConcurrentMap<Integer, String> concurrentMap = new ConcurrentHashMap<>();

        // 요소 추가
        concurrentMap.put(1, "One");
        concurrentMap.put(2, "Two");
        concurrentMap.put(3, "Three");

        // 요소 가져오기
        System.out.println("Key 1: " + concurrentMap.get(1));
        System.out.println("Key 2: " + concurrentMap.get(2));
        System.out.println("Key 3: " + concurrentMap.get(3));

        // 요소 제거
        concurrentMap.remove(2);

        // 요소가 제거되었는지 확인
        System.out.println("Key 2 after removal: " + concurrentMap.get(2));

        // 동시에 여러 스레드에서 맵 수정 가능
        Runnable runnable = () -> {
            for (int i = 0; i < 1000; i++) {
                concurrentMap.putIfAbsent(i, "Value" + i);
            }
        };

        // 여러 스레드에서 맵 수정
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        thread1.start();
        thread2.start();

        // 모든 작업이 끝날 때까지 기다림
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 맵의 크기 출력
        System.out.println("ConcurrentMap size: " + concurrentMap.size());
    }
}
  • Thread 관련 메소드

    • putIfAbsent() 메서드를 사용하여 여러 스레드가 안전하게 맵을 수정할 수 있다. 이를 통해 동시성 문제를 방지하면서 안전하게 맵을 조작할 수 있다.

    • thread1.join() , thread2.join()은 현재 실행 중인 스레드(일반적으로 메인 스레드)가 thread1이라는 스레드가 종료될 때까지 기다리도록 하는 메소드이다. 이 메소드는 예외처리를 해주어야 하므로 try-catch 블록 내에서 사용된다.

public JwtUser getLoginUser() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth.getPrincipal() instanceof JwtUser) return (JwtUser) auth.getPrincipal();
        throw new AccessDeniedException("권한이 없습니다.");
    }
  • getLoginUser(): Spring Security의 SecurityContextHolder를 사용하여 현재 로그인한 사용자의 정보를 가져온다. 인증 주체를 확인하고 주체가 JwtUser 타입인 경우에만 JwtUser 객체를 반환하며, 그렇지 않으면 AccessDeniedException을 발생시킨다.

    • SecurityContextHolder.getContext().getAuthentication(): 이 부분은 스프링 시큐리티의 SecurityContextHolder를 통해 현재 사용자의 인증 정보(Authentication)를 가져온다. SecurityContextHolder는 인증과 관련된 정보를 스레드 로컬 변수에 저장하고 제공하는 역할을 한다.

      AbstractAuthenticationToken : Spring Security에서 인증 과정을 추상화하고, 실제 인증된 주체와 관련된 정보를 다루며, 이를 토대로 인증된 사용자의 권한 등을 관리하는 데 사용된다. 주로 사용자 인증 및 권한 부여를 위한 기본적인 메서드와 동작을 정의하고 있다.

    • this.authorities = Collections.unmodifiableList(new ArrayList<>(authorities)); : AbstractAuthenticationToken 클래스에서 권한(authorities)을 불변(immutable)하게 만드는 과정을 수행한다.

profile
코딩 기록

0개의 댓글

관련 채용 정보