[Spring] 중복 로직 제거하기

이강혁·2024년 9월 5일
0

Spring

목록 보기
2/2
post-custom-banner

Spring 수업을 들으면서 현재 로그인한 사용자의 정보를 불러오는 로직이 반복되는 곳이 생겼다.

    public Cart readdetail(){
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        UserDetails userDetails = (UserDetails) auth.getPrincipal();
        Optional<Customer> coc = customerRepository.findByUsername(userDetails.getUsername());

        return cartRepository.findByUsername(coc.get().getUsername());
    }

위 코드에서 사용된 로직이 다른 서비스들에서도 같이 사용되었다. 그래서 따로 메서드로 만들어서 활용하고자 한다.

시도 1 - 실패

CustomerService라고 사용자의 로그인, 가입을 관리하는 서비스가 있는데 거기에 사용자 정보 불러오는 로직을 넣으면 좋을 것 같아서 시도했다.

    public Customer authen(){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        String username = userDetails.getUsername();

        Optional<Customer> oc = customerRepository.findByUsername(username);

        return oc.orElse(null);
    }

교수님도 이 방법을 알려주셨다. 하지만 실패했는데, CustomerService에서 CartService를 호출하고, CartService에서는 authen 메소드를 위해 CustomerService를 호출했기에 아래와 같은 오류코드를 보이며 중단되었다.

시도 2 - 실패

SecurityConfig 파일에는 비밀번호를 암호화하는 Bean이 등록되어있다.

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

그래서 이 방식을 사용하면 어떨까 싶어서 시도했다.

@Bean
String findUsername(){
	Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    UserDetails userDetails = (UserDetails) authentication.getPrincipal();
	return userDetails.getUsername();
}

Repository를 호출하는 부분을 빼기위해서 현재 로그인한 사용자의 이름만 찾아주고, 그 이후는 CustomerRepository를 호출해서 사용하는 방식을 시도했다.

하지만 실패했다. Bean으로 선언하면 초기화시점에 Bean을 생성하려고 하는데 그때는 로그인한 사용자의 정보도 없기도 하고, 무엇보다 SecurityContextHolder는 HTTP 요청 처리 중에만 인증 정보를 보유하기에 인증되지 않은 상태에서 호출될 수도 있다고 한다.

실제로도 authentication이 null이라서 오류가 발생했다는 응답이 나오기도 했다.

시도 3 - 성공?

React에서 하던 것처럼 util함수를 만들어서 활용해보고 싶었다. util 패키지를 생성하고 UserUtility라는 클래스를 만들었다. 그리고 안에 static으로 현재 로그인한 사용자의 정보를 찾는 메서드를 만들었다.

public class UserUtility {
    public static String findCurrentUsername(){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return userDetails.getUsername();
    }
}

static으로 선언함으로써 클래스의 인스턴스 만들어서 메서드를 호출하는 절차가 필요없도록 했다.
일단 작동은 했는데 Spring에서 일반적으로 사용하는 방식인지는 잘 모르겠다. 내일 교수님께 다시 여쭤보고 해야겠다.

시도 4 - GPT
GPT한테 SecurityConfig에 현재 사용자 정보 찾는 로직을 넣고 싶다고 했다. 그랬더니 2번에서 사용했던 로직을 둘로 나눈 방법을 알려주었다.

    @Bean
    public String getAuthenticatedUsername() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.isAuthenticated()) {
            Object principal = auth.getPrincipal();
            if (principal instanceof UserDetails) {
                return ((UserDetails) principal).getUsername();
            }
        }
        return null; // 인증되지 않은 경우 null 반환
    }

    @Bean
    public Optional<Customer> getAuthenticatedCustomer() {
        String username = getAuthenticatedUsername();
        if (username != null) {
            return customerRepository.findByUsername(username);
        }
        return Optional.empty();
    }

현재 사용자의 이름 찾는 Bean을 만들고, 이름으로 사용자 정보를 찾는 Bean을 만들었다.

 No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. 

하지만 String을 빈으로 등록하면 String으로 된 빈이 많아서 못 찾는다고 하는 것 같다.
왜인지 잘 모르겠으니 Spring강의를 다시 정주행 해야겠다.

profile
사용자불량
post-custom-banner

0개의 댓글