주노 님의 블로그

20241002 본캠프 55일차 TIL 본문

TIL

20241002 본캠프 55일차 TIL

juno0432 2024. 10. 2. 21:40

본캠프 55일차 내용 간단요약

  • 13:00 ~ 18:00 : 개인과제
  • 18:00 ~ 19:00 : 저녁시간
  • 19:00 ~ 21:00 : 개인과제

오늘 해야할 일 ✔️ 🔺 ❌

✔️개인과제 1단계 까지 듣기

 

더보기

Level 01

 

org.springframework.orm.jpa.JpaSystemException: could not execute statement [Connection is read-only. Queries leading to data modification are not allowed] [insert into todos (contents,created_at,modified_at,title,user_id,weather) values (?,?,?,?,?,?)]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1022) ~[spring-webmvc-6.1.12.jar:6.1.12]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.12.jar:6.1.12]

 

대충 위와같은 오류가 뜬다

at org.example.expert.domain.todo.service.TodoService.saveTodo(TodoService.java:39) ~[main/:na]

public TodoSaveResponse saveTodo(AuthUser authUser, TodoSaveRequest todoSaveRequest) {
    User user = User.fromAuthUser(authUser);

    String weather = weatherClient.getTodayWeather();

    Todo newTodo = new Todo(
            todoSaveRequest.getTitle(),
            todoSaveRequest.getContents(),
            weather,
            user
    );
    Todo savedTodo = todoRepository.save(newTodo);

    return new TodoSaveResponse(
            savedTodo.getId(),
            savedTodo.getTitle(),
            savedTodo.getContents(),
            weather,
            new UserResponse(user.getId(), user.getEmail())
    );
}

 

todo 위 메서드에서 오류가뜨는것을 발견했고

 

@Transactional(readOnly = true)

상단에 트랜잭셔널 readOnly ture가 되어있었다.

 

아니 읽기전용인데 너는 왜 쓰고있니? 라는 오류같았다.

위 메서드에 get todo 관련 메서드와 save todo 메서드가 하나만 있길래 save todo에 트랜잭션을 걸어주었다.

 

Level 02

user에 nickname 추가 jwt에 nickname 추가

 

JwtUtil.java
.claim("nickname", nickname)

JwtFilter.java
httpRequest.setAttribute("nickname", claims.get("nickname"));

AuthUserArgumentResolver.java
UserRole userRole = UserRole.of((String) request.getAttribute("userRole"));

User.java
    public static User fromAuthUser(AuthUser authUser) {
        return new User(authUser.getId(), authUser.getEmail(),authUser.getNickname(), authUser.getUserRole());
    }
    
AuthUser.java
    private final UserRole userRole;

 

그외 우수수수수...

 

토큰을 만들때 nickname을 claim으로 넣어주고, authuser에서 받아오는 코드에 모드 nickname을 넣어준다.

 

level03

UserAdminController 클래스의 changeUserRole() 메소드가 실행 전 동작해야해요.
AdminAccessLoggingAspect 클래스에 있는 AOP가 개발 의도에 맞도록 코드를 수정해주세요.

@After("execution(* org.example.expert.domain.user.controller.UserController.getUser(..))")

 

@After 어노테이션이 되어있다.

@Before 어노테이션을 달아주자.

httpRequest.setAttribute("userRole", claims.get("userRole"));

 

또한 userole을 조회하기위해

 

if(request.getAttribute("userRole").equals("ADMIN"))
{
    log.info(
        "Admin Access Log - User ID: {}, Request Time: {}, Request URL: {}, Method: {}",
        userId, requestTime, requestUrl, joinPoint.getSignature().getName());
}

 

위처럼 작성하였다

 

user controller의 getuser을 사용해서 조회를 해보자.

결과는 아래와같다

 

2024-10-02T16:14:55.013+09:00  INFO 4768 --- [nio-8080-exec-3] o.e.expert.aop.AdminAccessLoggingAspect  : Admin Access Log - User ID: 5, Request Time: 2024-10-02T16:14:55.013127800, Request URL: /users/5, Method: getUser
[Hibernate] 
    select
        u1_0.id,
        u1_0.created_at,
        u1_0.email,
        u1_0.modified_at,
        u1_0.nickname,
        u1_0.password,
        u1_0.user_role 
    from
        users u1_0 
    where
        u1_0.id=?

 

level04

컨트롤러 테스트코드가 실패한다고 한다

 

@Test
void todo_단건_조회_시_todo가_존재하지_않아_예외가_발생한다() throws Exception {
    // given
    long todoId = 1L;

    // when
    when(todoService.getTodo(todoId))
            .thenThrow(new InvalidRequestException("Todo not found"));

    // then
    mockMvc.perform(get("/todos/{todoId}", todoId))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.status").value(HttpStatus.OK.name()))
            .andExpect(jsonPath("$.code").value(HttpStatus.OK.value()))
            .andExpect(jsonPath("$.message").value("Todo not found"));
}

 

코드를 보면 when에서 then throw를 던지고는

perform에서는 isOk를 날리는것을 볼 수 있다.

 

@Test
void todo_단건_조회_시_todo가_존재하지_않아_예외가_발생한다() throws Exception {
    // given
    long todoId = 1L;

    // when
    when(todoService.getTodo(todoId))
            .thenThrow(new InvalidRequestException("Todo not found"));

    // then
    mockMvc.perform(get("/todos/{todoId}", todoId))
            .andExpect(status().is4xxClientError())
            .andExpect(jsonPath("$.status").value(HttpStatus.BAD_REQUEST.name()))
            .andExpect(jsonPath("$.code").value(HttpStatus.BAD_REQUEST.value()))
            .andExpect(jsonPath("$.message").value("Todo not found"));
}

 

400error로 바꿔주자.

 

level 05

todo 조회에서 weather과 update date를 기준으로 검색하는 코드로 바꾸세요

 

public Page<TodoResponse> getTodos(int page, int size, String weather, String startUpdateDate, String endUpdateDate)
{
    startUpdateDate += " 00:00:00";
    endUpdateDate += " 23:59:59";
    Pageable pageable = PageRequest.of(page - 1, size);

    LocalDateTime startLocalDate = LocalDateTime.parse(startUpdateDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    LocalDateTime endLocalDate = LocalDateTime.parse(endUpdateDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

    Page<Todo> todos = todoRepository.findAllByWeatherAndBetweenUpdateDate(pageable, weather, startLocalDate, endLocalDate);

 

date를 쿼리 파라미터로 받아준다음 auditing된 date가 localdatetime이라 localdatetime으로 변환해준다.

 

    @Query("SELECT t FROM Todo t"
        + " LEFT JOIN FETCH t.user u"
        + " WHERE (:weather IS NULL OR t.weather = :weather) AND t.modifiedAt between :startLocalDate and :endLocalDate"
        + " ORDER BY t.modifiedAt DESC")
    Page<Todo> findAllByWeatherAndBetweenUpdateDate(Pageable pageable, String weather,
        LocalDateTime startLocalDate, LocalDateTime endLocalDate);

 

쿼리는 이렇게 만들어준다

where 에서 weather과 같거나, null이고, modified at이 사이값이라면..

 

참고자료

https://developer-joon.tistory.com/171

 

[JPA] 쿼리 파라미터 값으로 동적인 결과를 반환할 때 null값 처리(검색 필터링)

예를 들어 category를 선택하고 검색을 할 수 있어서 2가지 이상으로 필터링을 한다면 '?category=운동&search=철봉' 이렇 쿼리 파라미터가 추가된다. 하지만 필터링을 한 가지만 쓴다면 '?category=운동&sea

developer-joon.tistory.com

 

level6

눅아 실수로 지워!!!!!!!!!!!!!!!!!!!

 

cascade타입을 추가해서

todo가 추가되면 manager로 자동 추가되게 하라

@OneToMany(mappedBy = "todo", cascade = {CascadeType.REMOVE, CascadeType.PERSIST})
private List<Manager> managers = new ArrayList<>();

 

 cascade 타입에 대해서 짚고 넘어가자

 

PERSIST : 부모 엔티티가 저장될때 자식 엔티티도 같이 저장된다

MERGE : 부모 엔티티가 병합 될때 자식 엔티티도 병합이된다

REMOVE : 부모 엔티티가 삭제될때 자식엔티티도 삭제

REFRESH : 부모 엔티티가 새로고침 될때 자식 엔티티도 새로고침 됨(?)

DETACH : 부모 엔티티가 영속성 컨텍스트에서 분리될때, 부모엔티티와 자식엔티티도 별개로 분리된다(?)

ALL : 위를 모두 가져가는것

 

PERSIST만 추가하면되지만, 삭제까지 같이하면 좋으니까..

 

LEVEL7

@Query("SELECT c FROM Comment c JOIN c.user WHERE c.todo.id = :todoId")
List<Comment> findByTodoIdWithUser(@Param("todoId") Long todoId);

[Hibernate] 
    select
        c1_0.id,
        c1_0.contents,
        c1_0.created_at,
        c1_0.modified_at,
        c1_0.todo_id,
        c1_0.user_id 
    from
        comments c1_0 
    where
        c1_0.todo_id=?
2024-10-02T20:36:15.565+09:00 TRACE 38116 --- [nio-8080-exec-1] org.hibernate.orhttp://m.jdbc.bind : binding parameter (1:BIGINT) <- [1]
[Hibernate] 
    select
        u1_0.id,
        u1_0.created_at,
        u1_0.email,
        u1_0.modified_at,
        u1_0.nickname,
        u1_0.password,
        u1_0.user_role 
    from
        users u1_0 
    where
        u1_0.id=?
2024-10-02T20:36:15.579+09:00 TRACE 38116 --- [nio-8080-exec-1] org.hibernate.orhttp://m.jdbc.bind : binding parameter (1:BIGINT) <- [8]
[Hibernate] 
    select
        u1_0.id,
        u1_0.created_at,
        u1_0.email,
        u1_0.modified_at,
        u1_0.nickname,
        u1_0.password,
        u1_0.user_role 
    from
        users u1_0 
    where
        u1_0.id=?

 

첫번째 쿼리문을 제외한 아래 두 쿼리는

user를 찾는 쿼리가 나가는것을 볼 수 있다.

@Query("SELECT c FROM Comment c LEFT JOIN FETCH c.user WHERE c.todo.id = :todoId")
List<Comment> findByTodoIdWithUser(@Param("todoId") Long todoId);

 

[Hibernate] 
    select
        c1_0.id,
        c1_0.contents,
        c1_0.created_at,
        c1_0.modified_at,
        c1_0.todo_id,
        c1_0.user_id,
        u1_0.id,
        u1_0.created_at,
        u1_0.email,
        u1_0.modified_at,
        u1_0.nickname,
        u1_0.password,
        u1_0.user_role 
    from
        comments c1_0 
    left join
        users u1_0 
            on u1_0.id=c1_0.user_id 
    where
        c1_0.todo_id=?

 

쿼리문 하나로 바뀐것을 볼 수 있다

 

 

'TIL' 카테고리의 다른 글

20241007 본캠프 57일차 TIL  (0) 2024.10.07
20241004 본캠프 56일차 TIL  (0) 2024.10.04
20241001 본캠프 54일차 TIL  (1) 2024.10.01
20240930 본캠프 53일차 TIL  (0) 2024.09.30
20240927 본캠프 52일차 TIL  (0) 2024.09.27