[main-project] 0322

박채은·2023년 3월 22일

Project

목록 보기
19/21

로그인

비로그인 시에도, 게시글을 볼 수 있도록 수정

유저가 로그인 상태이던, 비로그인 상태이던 모두 게시글 리스트를 볼 수 있도록 API를 수정했다.
처음에는 로그인의 경우와 비로그인의 경우 API를 각자 만드려고 했는데 코드 중복도 심할 것 같아 API에 조건을 추가해서 로그인/비로그인 상태를 체크했다.

API에서 로그인했는지의 여부는 3가지 경우로 확인할 수 있다. (참고)

  • SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  • 메서드 인자로 Principal principal
  • 메서드 인자로 @AuthenticationPrincipal Member member
    ('@AuthenticationPrincipal' not applicable to local variable)

Controller 메서드 내부에서 로그인했는지를 확인해야 하기 때문에 첫번째 방법을 사용해서 로그인 여부를 체크해주었다.

비로그한 사용자는 SecurityContextHolder.getContext().getAuthentication().getPrincipal()의 값이 null일 줄 알았는데 디버깅해서 출력해보니 anonymousUser이었다.

찾아보니 비로그인한 사용자는 Authentication이 null 이 아니고 문자열의 "anonymousUser" 이 저장되어 있는 principal 과 ROLE_ANONYMOUS 권한 정보를 가지고 있는 객체라고 한다.

Object principalObj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if(principalObj == "anonymousUser"){
    // 비로그인 상태인 경우
}

위와 같이 코드를 작성했고, 잘 구현되었다.


@AuthenticationPrincipal으로 리팩토링

SecurityContextHolder.getContext().getAuthentication().getPrincipal() 대신에 @AuthenticationPrincipal@Nullable를 붙이는 게 더 깔끔할 것 같아서 수정했다.

public ResponseEntity getPrfPosts(@Nullable @AuthenticationPrincipal Member member,
                                  @Positive @RequestParam(defaultValue = "1") int page,
                                  @Positive @RequestParam(defaultValue = "10") int size,
                                  @Positive @RequestParam(defaultValue = "1") int sorting){
     if(member == null){ // 비로그인 시에
            List<PrfPostDto.Response> result = customMapper.prfPostsToResponseDtos(allPrfPost);
            return new ResponseEntity<>(new MultiResponseDto<PrfPostDto.Response>(result, pageInfo), HttpStatus.OK);
     }
}

@AuthenticationPrincipal

현재 구현 상태

  • 메서드 인자에 Principal principal을 두어, 로그인한 사용자의 정보를 가져온다.
  • 단점) 로그인한 사용자의 이름(email)만 가져올 수 있다.

개선

  • @AuthenticationPrincipal 애너테이션을 통해서 로그인한 사용자의 다양한 정보를 가져오게끔 구현한다.
  • 코드가 더 깔끔해질 것 같고, 필요한 정보들도 같이 가져올 수 있다.
  • 팀원분이 잘 설정해두셔서 @AuthenticationPrincipal에 null값 들어오는 문제는 발생하지 않았지만, 해당 문제가 왜 발생하는지 이해하는 것도 중요할 것 같다.

디버깅 문제

디버깅을 하는데 디버깅 속도도 느려지고 계속 Collecting data라는 문구가 뜨면서 데이터가 노출되지 않았다.
1000만건 되는 데이터들을 계속 불러와서 그런지 디버깅이 느려지고 제대로 되지 않았다.

우선 시도한 방법은 다음과 같다.

  • breakpoint 최소화(모두 지우기)
    • 확실히 어느정도 속도 향상에 영향을 미쳤다. 하지만 내가 설정해둔 breakpoint는 최대 4개정도였는데 이렇게 적게 설정해놔도 느리다는 것이 이상해서 다른 이유가 있지 않을까 싶었다.
  • Method breakpoints 지우기
    • Method breakpoints가 디버깅을 확실히 느리게 만들었다.
  • Setting 수정
    • Enable alternative views for Collections classes and Enable toString() object view options을 disable하게 설정해뒀다.

Method breakpoints를 지우고, Setting을 수정해주니까 디버깅 속도가 확실히 나아졌다.

[참고]
jetbrains
jetbrains
stackoverflow - IntelliJ IDEA stucks at "collecting data" while debug


Thread starvation or clock leap detected

HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=1m6s443ms).

DB에 데이터를 1000만건을 넣은 상태에서 잘 돌아가던 코드를 돌리니 다음과 같은 에러가 떴다.

여러 블로그나 글들을 참고해봤는데 원인은 다음과 같다.

https://stackoverflow.com/questions/38703876/log-warning-thread-starvation-or-clock-leap-detected-housekeeper-delta-springh
https://github.com/brettwooldridge/HikariCP/issues/679

  1. Mac 사용 시, 앱을 로컬로 실행하고 컴퓨터가 절전 모드로 전환되었기 때문에
    https://sudo-minz.tistory.com/85

  2. 과도한 GC(가비지 컬렉션)으로 인해 가비지 컬렉션이 하우스키핑 스레드의 실행보다 더 오랜 시간 동안 실행되고 일부 메모리를 확보하려고 시도하는 경우

  3. 가상 컨테이너(VMWare, AWS 등)에서 실행 중일 수 있습니다. 가상 컨테이너는 어떤 이유로 시간 흐름의 환상을 유지하는 데 특히 열악합니다.

  4. housekeeper 스레드에서 다른 일이 발생하기 때문에, housekeeper 스레드가 blocking 될 수 있다.

  5. 모든 CPU가 고정된 상태에서 서버가 너무 바빠서 스레드 고갈(Thread starvation)이 발생하여 housekeeper 스레드가 housekeeping 기간 동안 실행되지 않습니다.

  6. 응용 프로그램을 디버깅하고 한 중단점에서 두 번 이상의 관리 기간(60초) 이상 대기하는 경우에도 발생합니다.

  1. 서버 사양이 부족하기 때문에
    • stackoverflow에서 ec2의 메모리나 CPU를 업글했더니 해결되었다는 사람도 있었다.
  1. connection pool이 부족하기 때문에
    https://velog.io/@morningstar/server-bumb
    https://velog.io/@mbsik6082/Thread-starvation-or-clock-leap-detected-Dead-Lock-hikari-%EC%98%A4%EB%A5%98
    우아한 - HikariCP Dead lock에서 벗어나기

여러 원인들이 존재하다보니, 하나하나 시도해보지 못했는데 갑자기 어느 순간 에러가 사라졌다.(도대체 왜??)
Mac 절전(잠자기) 모드도 영향을 미치는 것 같지만, 데이터 1000만건으로 인해 데이터베이스 쿼리가 느리거나 시스템 리소스(connection pool이나 CPU)가 부족해서 발생한 것 같다.

0개의 댓글