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

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

codingTrip 2025. 2. 25. 21:24

코드카타

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

나의 풀이

import java.util.*;
class Solution {
          public String[] 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());
            System.out.println("sortList = " + sortList);

            // 정렬된 리스트에서 문자열만 추출하여 배열로 변환
            answer = sortList.stream().map(s -> s.str).toArray(String[]::new);
            return answer;
        }

        class SortNumComparator implements Comparator<Sort>{
            @Override
            public int compare(Sort o1, Sort o2) {
                // num 비교
                int numCompare = Integer.compare(o1.num, o2.num);
                if (numCompare != 0) {
                    return numCompare;
                }
                // num이 같다면 str 기준으로 정렬
                return o1.str.compareTo(o2.str);
            }
        }

        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 + " ]";
            }
        }

}

answer = sortList.stream().map(s -> s.str).toArray(String[]::new);

Integer.compare(o1.num,o2.num)

 

 

다른 분들의 풀이

import java.util.*;

class Solution {
    public String[] solution(String[] strings, int n) {
        String[] answer = {};
        ArrayList<String> arr = new ArrayList<>();
        for (int i = 0; i < strings.length; i++) {
            arr.add("" + strings[i].charAt(n) + strings[i]);
        }
        Collections.sort(arr);
        answer = new String[arr.size()];
        for (int i = 0; i < arr.size(); i++) {
            answer[i] = arr.get(i).substring(1, arr.get(i).length());
        }
        return answer;
    }
}

나의 풀이

출처:https://school.programmers.co.kr/learn/courses/30/lessons/12915/solution_groups?language=java

substring을 사용하셨다...

나는 이를 위해 map부터 다양한 것을 생각했었는데 말이다.


2025.02.25(화)

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

도전

Lv 4. API 로깅 - Interceptor

Interceptor를 사용해서 구현하고자 했다.

아래의 코드처럼 실행하니

권한이 ADMIN임에도 equals가 false가 되었다.

if (!UserRole.ADMIN.equals(userRole)){
    log.error("미인증 사용자 요청");
    response.sendError(HttpServletResponse.SC_FORBIDDEN, "관리자 권한이 없습니다.");
    return false;
}

이 조건문이 잘 실행되어야 제대로 관리자 권한인지 체크할 수 있는데... 하면서 원인을 찾아보았다.

 

원인은 각 비교군의 타입이 다르기 때문이었다.

UserRole.ADMIN은 Enum 타입이고

userRole은 String 타입으로 강제 형변환했으므로 String 타입이었다.

String userRole = (String) request.getAttribute("userRole");

따라서 Enum -> String 타입으로 형변환하거나

String -> Enum 타입으로 형변환 해주어야 했다.

나의 경우는 Enum -> String 타입으로 형변환하기로 했다.

 

Enum.name() vs Enum.toString()

if (!UserRole.ADMIN.name().equals(userRole)){
    log.error("미인증 사용자 요청");
    response.sendError(HttpServletResponse.SC_FORBIDDEN, "관리자 권한이 없습니다.");
    return false;
}

Enum -> String 타입으로 형변환하는 방법은 크게 2가지가 있는데

나는 이중에서 Enum.name()을 사용했다.

그 이유는 name()은 final 메소드이므로 재정의할 수 없지만 toString()은 재정의가 가능하기 때문이다.

재정의가 불가능한 것이 더 안전하다고 판단했다.

출처 : https://velog.io/@nhj2927/Java-Enum.name-vs-Enum.toString

 

Lv 5. 위 제시된 기능 이외 ‘내’가 정의한 문제와 해결 과정

회원가입 후, 바로 JWT가 발급되는 점

1. 문제 인식 및 정의

 : 회원가입 후, 바로 RequestBody에 JWT 발급됨

 

이를 문제로 인식한 이유 :

 - 보안 문제: 사용자의 인증 정보가 아직 완전히 검증되지 않은 상태에서 토큰이 생성될 가능성이 존재함
현재 코드에서는 서비스에서 아래와 같이 이미 존재하는 이메일인지 확인하는 검증 로직만 존재함

if (userRepository.existsByEmail(signupRequest.getEmail())) {
    throw new InvalidRequestException("이미 존재하는 이메일입니다.");
}

해당 코드에는 구현되어있지 않으나

추후 탈퇴 계정 확인 후 차단, 회원가입 시도 -> 관리자 승인 후 회원가입 완료 등의 기능 구현 시 문제 발생 가능성 존재함

- 로그인 시에도 JWT 발급:  로그인 시에 사용자의 이메일, 비밀번호 등을 검증한 후 JWT를 발급하는 것이 더 안전함

- 불필요한 토큰 생성: 회원 가입 후 사용자가 바로 로그인 하지 않으면, 불필요한 토큰 생성 가능성 존재함

- 토큰 관리의 어려움: JWT는 한 번 발급되면 서버에서 무효화하기 어려움.  따라서 회원 가입 시점에 발급된 토큰은 관리가 어려울 가능성 존재함.


2. 해결 방안
2-1. 의사결정 과정

회원 가입 후에는 JWT를 발급하지 않고, 로그인 시에만 발급하는 것으로 수정


2-2. 해결 과정

회원 가입 후 ResponseBody에는 회원의 아이디, 이메일, 역할 정보만 보여주는 것으로 수정

 

* 수정된 파일 목록
org.example.expert.domain.auth.dto.response.SignupResponse

 
/** 수정 후
 * 회원 가입 후 ResponseBody에는 회원의 아이디, 이메일, 역할 정보만 보여주는 것으로 수정
 */
private final Long id;
private final String email;
private final UserRole userRole;

public SignupResponse(Long id, String email, UserRole userRole) {
    this.id = id;
    this.email = email;
    this.userRole = userRole;
}

 

org.example.expert.domain.auth.service.AuthService

/** 수정 후
 * 회원 가입 후 ResponseBody에는 회원의 아이디, 이메일, 역할 정보만 보여주는 것으로 수정
 */
return new SignupResponse(savedUser.getId(),savedUser.getEmail(),savedUser.getUserRole());


3. 해결 완료
3-1. 회고

처음에는 '왜 회원가입 후, 로그인 후에도 JWT가 발급되지?' '단순히 보통 로그인 시에 JWT가 발급되지 않나?'

라는 막연한 생각과 단순한 의문만 가지고 있었으나

좀 더 회원가입 직후 JWT 발급 시 발생 가능한 문제점들을 찾아보니

더욱 나의 생각에 확신을 가질 수 있었다.

앞으로는 궁금한 부분에 대해 의문을 갖고 찾아보는 습관을 들여야겠다.

 

3-2. 전후 데이터 비교

수정 전 수정 후
수정 전
수정 후

 

Lv 6. 테스트 커버리지

이 단계는 완벽하게 소화할 것이라고 생각하지는 않고,

다만 일단 오늘 배운 테스트코드 실습 세션 자료를 따라 쳐보면서

테스트코드를 작성하는 것에 익숙해지고자 한다.

 

could not prepare statement [Table "USERS" not found (this database is empty); SQL statement:

출처 : https://stackoverflow.com/questions/72959168/spring-boot-h2-db-for-testing-throws-table-not-found-and-sql-syntax-error

원인은 application.properties에 mysql 관련 설정을 해준 것이 원인인 것으로 보인다.

default로 테스트 코드는 H2를 사용하므로 해당 부분을 주석처리 해주고 실행했다.

 

main과 test를 분리해서 따로 실행하는 부분은 추후 더 알아봐야겠다.