CODING/스파르타 내일배움캠프 TIL

47_문자열 내 마음대로 정렬하기_Spring 심화 주차 개인 과제 트러블슈팅_25.2.24(월)

codingTrip 2025. 2. 24. 23:07

코드카타

47) 문자열 내 마음대로 정렬하기

List<Sort> Comparator를 활용해서 정렬까지는 구현했다.

import java.util.*;

public class Ex47 {
    static class Solution {
        public List<Sort> solution(String[] strings, int n) {
            String[] answer = {};

            List<Sort> sortList = new ArrayList<>();

            for (String s : strings) {
                sortList.add(new Sort(s,s.charAt(n)));
            }

            Collections.sort(sortList, new SortNumComparator());
            Collections.sort(sortList, new SortStrComparator());

            return sortList;
        }

        class SortNumComparator implements Comparator<Sort>{
            @Override
            public int compare(Sort o1, Sort o2) {
                if (o1.num > o2.num){
                    return 1;
                } else if (o1.num < o2.num){
                    return -1;
                }
                return 0;
            }
        }

        class SortStrComparator implements Comparator<Sort>{
            @Override
            public int compare(Sort o1, Sort o2) {
                return o1.str.compareTo(o2.str);
            }
        }

        class Sort {
            private String str;
            private int num;

            public Sort(String str, int num) {
                this.str = str;
                this.num = num;
            }

            @Override
            public String toString() {
                return "[ " + this.str + ": " + this.num + " ]";
            }
        }

        public static void main(String[] args) {
            Solution sol = new Solution();
            System.out.println(sol.solution(new String[]{"abce", "abcd", "cdx"},2));
        }
    }
}

 

출력값은 아래와 같다.

[[ abcd: 99 ], [ abce: 99 ], [ cdx: 120 ]]

내가 원하는 대로 정렬은 잘 되었다.

 

문제는... 사실 반환값이 String[]이었고...

따라서 결과가 ["abcd", "abce", "cdx"]로 나와야 한다는 점이다.

 

과제에 마음이 급해서 오늘은 여기까지 하고자 한다.


2025.02.24

Spring 심화 주차 개인 과제 트러블슈팅

필수

Lv 1-3. Validation

사실 이전 개인과제에서 정규식을 적용하지 않고 기본으로 주어지는 Validation을 사용했기 때문에

정규식에 대해 익숙하지 않았던 것은 사실이다.

따라서 블로그로 검색해서 정규식에 대한 개념을 이해하고,

내가 이해한 바를 정규식으로 만들어 아래와 같이 테스트를 진행했다.

정규식 사이트에서 테스트 진행

https://regexr.com/

새 비밀번호는 8자 이상이어야 하고, 숫자와 대문자를 포함해야 합니다.

나는 위의 조건에 대해

8자 이상이고,

숫자와 대문자는 무조건 포함해야 하고,

나머지 소문자와 특수문자도 입력 가능하게 해야한다고 이해했다.

따라서 위와 같은 정규식으로 만들었다.

 

Lv 2. N+1 문제

먼저 각각의 개념에 대해 파악하는 것이 중요하다고 생각했다.

 

N+1 문제란?

JPA가 연관된 엔티티를 조회할 때 추가적인 쿼리를 반복적으로 실행하기 때문에 발생하는 문제

하나의 쿼리로 N개의 객체를 로딩한 후,

각 객체에 연관된 데이터를 추가로 조회하는 개별 쿼리가 N번 실행되면서 총 N+1번의 쿼리가 발생하는 문제

 

Fetch Join이란?

연관된 엔티티나 컬렉션을 SQL 한번으로 조회할 수 있도록 해주는 기능

한 번의 쿼리로 연관된 엔티티들을 함께 로드할 수 있음

 

@EntityGraph란? 

Data JPA에서 fect 조인을 어노테이션으로 사용할 수 있도록 만들어 준 기능

 

=> N+1 문제를 해결하는 방법에는 Fetch Join, @EntityGraph등이 있다.

과제에서는 @EntityGraph로 변경하는 것이었기 때문에 아래와 같이 수정했다.

사실 users(테이블명)인지 user(엔티티 필드명)인지 혼란스러웠다.

검색을 통해 엔티티 필드명이라는 것을 찾을 수 있었다.

@EntityGraph(attributePaths = {"user"})

 

Lv 3. 테스트코드 연습 -2-3

nullSafeEquals

https://docs.spring.io/spring-framework/docs/1.2.x/javadoc-api/org/springframework/util/ObjectUtils.html#nullSafeEquals(java.lang.Object,%20java.lang.Object)

둘 다 null이면 true

둘 다 null이 아니면 equals()

둘 중 하나가 null이면 false를 반환한다.

if (!ObjectUtils.nullSafeEquals(user.getId(), todo.getUser().getId())) {
    	throw new InvalidRequestException("담당자를 등록하려고 하는 유저가 일정을 만든 유저가 유효하지 않습니다.");
}

따라서 위와 같은 조건을 보면서

'둘 중 하나가 null이니까 InvalidRequestException이 발생할 것 같은데 왜 안 되는 거지?'

라는 의문이 들었고, 이에 시간을 많이 쓰게 되었다.

 

if (!ObjectUtils.nullSafeEquals(
        user.getId(), 
        todo.getUser().getId())
) {
    	throw new InvalidRequestException("담당자를 등록하려고 하는 유저가 일정을 만든 유저가 유효하지 않습니다.");
}

정확한 원인 분석을 위해 위의 코드처럼 줄을 나눠 보았다.

org.opentest4j.AssertionFailedError: Unexpected exception type thrown, 
Expected :class org.example.expert.domain.common.exception.InvalidRequestException
Actual   :class java.lang.NullPointerException
Caused by: java.lang.NullPointerException: Cannot invoke "org.example.expert.domain.user.entity.User.getId()" because the return value of "org.example.expert.domain.todo.entity.Todo.getUser()" is null

 

그랬더니 todo.getUser().getId()이 부분에서 문제가 발생했음을 파악할 수 있었다.

todo에서 getUser가 null이고, 이를 getId()를 하다보니

NullPointerException이 발생한 것이었다.

 

따라서 아래와 같이 기존 if문 조건에 todo.getUser가 null인 경우도 추가했다.

if (todo.getUser() == null || !ObjectUtils.nullSafeEquals(user.getId(), todo.getUser().getId())) {
    	throw new InvalidRequestException("담당자를 등록하려고 하는 유저가 일정을 만든 유저가 유효하지 않습니다.");
}

 

 

내일 테스트코드 실전 특강이 있을 예정인데

열심히 잘 들어서 과제를 잘 마무리 해야겠다.