SPARTA 과제/SPRING

숙련) 일정관리앱 트러블슈팅 (1)

kjw81024 2026. 2. 11. 20:38

트러블이 너무 많다.... ..... 문제덩어리 인간이 된거같은 느낌


트러블 슈팅

문제1

Schedule Request DTO 를 작성할 때 user_id 를 넣어야하는가?

로그인에 성공하기만 하면 그 정보를 저장하고 있을테니까 알아서 저장해줄 수 있는 거 아닌가

 

해결과정

로그인에 성공하면 어디 정보를 저장하고 있는지 다시 생각해볼 필요가 있을 것 같다...

세션/쿠키로 로그인을 구현하는게 목표
-> 서버의 메모리나 DB에 세션 ID 를 저장해둠/ 브라우저는 세션 ID를 쿠키에 저장 
-> 브라우저가 서버에 세션ID 만 보냄 
-> 서버에서 세션 ID 비교 

서버에서 세션 ID를 비교하고 저장해둬야한다. 

저장 방법: HttpSession session 을 가져오고 아래코드처럼 Controller 에서 sessionUser Entity 형태로 저장

session.setAttribute("loginUser", sessionUser);
@Getter
public class SessionUser {
    private final Long id;
    private final String name;
    private final String email;

    public SessionUser(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
}

 

그럼 세션ID를 저장해두는 공간을 만들어야하나? 

-> 따로 설정하지 않으면 "TOMCAT" 이라는 서버 메모리에 아래처럼 저장됨

KEY		{ VALUE }
sessionId1 	{ loginUser: (SessionUser1) }
sessionId2 	{ loginUser: (SessionUser2) }
sessionId3 	{ loginUser: (SessionUser3) }

약간 이중 MAP 이라고 생각하면 편할듯하다

sessionId 가 TOMCAT 레벨에서의 KEY 가 되고 

그 안에서 loginUser 라는 String 이 다시 KEY 가 되어 SessionUser 를 찾을 수 있게 된다.

 

결론

request 에 userId를 넣을 필요는 없고 로그인 하면 쿠키에 JSESSIONID 가 오기때문에 이 쿠키에서 ID 를 기준으로 user 찾기


문제2

login에 성공하면 자동으로 jpa 가 로그인된 유저인지 확인을 해주나..?

아닐거같음

어떻게 컨트롤러에서 SessionId를 확인해줄 수 있는가

 

해결과정

생각해보면 Session ID 만 보내는게 아닌데 바로 확인을 해주지 않는다.

 

SessionUser 를 HttpSession 으로부터 getAttribute 를 사용해서 가져오기 

만약에 sessionUser (로그인 목록) 에 보낸 JSESSIONID 가 존재하지 않는다면 (null) 예외처리 

ResponseEntity<ResponseDTO> methodName(@Valid @RequestBody RequestDTO request, HttpSession session){
    SessionUser sessionUser = (SessionUser) session.getAttribute("loginUser");
    if( sessionUser == null){
        throw new BeforeLoginUserException();
    }
    # method 내용 후략
}

 

결론

JPA 가 자동으로 로그인 되었는지 확인 불가능, sessionID만 들어오니까 확인을 해줘야한다.

컨트롤러에서는 getAttribute 를 사용해서 SessionUser 를 가져오고 로그인 가능한지 직접 확인


문제3

근데 그럼 생성 수정 삭제 (login 이 필요한 행동) 할 때 마다 SessionId를 확인해줘야하는가...

 

해결과정

함수를 만들어서 Controller 에서 확인하는게 나을까 해서 함수를 작성해봤다. 

SessionUser checkAuthorizeUser(HttpSession session){
	SessionUser sessionUser = (SessionUser) session.getAttribute("loginUser"); 
    if( sessionUser == null){
    	throw new BeforeLoginUserException(); 
    }
    return sessionUser;
}

하지만 저렇게 구현하면 어쨌든 Controller 에서 매번 checkAuthorizedUser 함수를 호출해줘야하기 때문에 번거로워지고 InterCeptor 를 실무에서 사용한다고 한다.

Interceptor 가 뭔지 더 검색해봤다.

 

너무 깊게 들어가는 것 같아서 튜터님께 여쭤보니 이후에 이 부분은 Spring Security 와 함께 배운다고 넘어가라고 하셨습니다. . 언젠가 해보는걸로 !! 했다.

 

아래는 intercepter 구현해본것... 무시하면 됨 

더보기

interceptor 는 우리가 알고있던 요청 - Controller - Service - Repository - Service - View 순서에서 중간중간 intercept (가로채다, 차단하다 의미) 하여 다음단계로 넘어갈지 말지 결정할 수 있게한다.

출처: https://hyejin.tistory.com/280

내가 사용할건 preHandle() 로 Controller 안에서 getAttribute 로 다 체크해주고 있었던 부분을 Interceptor 로 구현하여 매번 getAttribute 하지 않을 수 있게 할 수 있다.

 

여기서 고민이 되었던 부분이 

POST 요청같은 경우 -> 로그인이 되어있는지만 확인하면 됨

PUT, DELETE 요청같은 경우 -> 로그인이 되어있는지 + 현재 접근하려는 글의 작성자인지도 확인해야함 

이었다

일단 공통적으로 로그인이 되어있는 상태인지는 확인해야하므로 interceptor 코드를 작성했다.

CustomInterceptor 를 만들고 HandlerInterceptor 를 구현하게 만듦

@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        HttpSession session = request.getSession(false);
        if(session == null){
            throw new BeforeLoginUserException();
        }

        SessionUser user = (SessionUser) session.getAttribute("loginUser");
        if (user == null) {
            throw new BeforeLoginUserException();
        }
        return true;
    }
}

참고로 request 만 쓰길래 response 와 handler 는 지웠더니 오류가 났다

원래부터 인터페이스가 아래 형태로 지정되어있어서 override 하는 입장에선 바꾸면 안 된다.

# 매개변수: create
# 	true: session 이 존재하지 않으면 새 세션을 생성하여 반환
# 	false: session 이 존재하지 않으면 null 반환
HttpSession getSession(boolean create);

원래는 getSession 뒤 인수를 default (true) 로 했었는데 현재 로그인이 되어있는지 확인하는게 목적이니까 새 세션을 만드는것이 아니라 null 을 반환해줘야한다. -> session 이 null 인지도 확인 

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    private final LoginCheckInterceptor loginCheckInterceptor;

    public MvcConfig(LoginCheckInterceptor loginCheckInterceptor) {
        this.loginCheckInterceptor = loginCheckInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/signup")
                .excludeHttpMethods(HttpMethod.GET);
    }
}

열심히 만든 loginCheckInterceptor 를 실행시키기 위해 MvcConfig 클래스를 만들고 WebMvcConfigurer 를 구현하게 했다.

addInterceptors 를 사용해서 무슨 interceptor 를 언제 실행시키고 언제 실행시키지 않을지 설정해줬다.

 

추가로 우리는 sessionUser 의 ID 를 Service 계층으로 보내야하는 문제가 남아있다...

Controller 에서 세션을 바로 분석해서 썼을때는 안 해도 됐지만 이젠 create 의 매개변수로 sessionId 를 넘겨줘야한다.

 

 

결론

아직까지의 단계에서는 Controller 에서 하나하나 확인해줘도 괜찮다는 튜터님의 답변을 들었습니다!


문제4

SessionUser 를 보낼까 SessionUser ID 를 보낼까

sessionUser 로 받아온 값을 service 에서 새로운 객체 user 로 만들어서 넣는다고 하면 다른 객체가 되어버리면 어쩌지 id만 받아와서 찾아서 넣는게 맞는거겠지


해결과정

ID 만 보내는 게 좋을 것 같다는 생각을 했다

상관없을거같긴한데 ... 왜냐면 객체 자체를 저장하는거니까 

그래도 왜 그렇게 생각했냐면 SessionUser 자체는 어차피 User table 에서 가져오기 때문

 

회원가입 시 table 에 정보 저장

하지만 이후 User 의 이름이나 닉네임등 (지금 프로젝트엔 없지만) 필드가 업데이트 되었을 때 SessionID 가 담긴 곳도 업데이트가 될 지 정확하게 모르겠어서 업데이트 되지 않는 고유한 값인 ID 만 넘겨서 이후 요청 (일정 생성) 등에 User name 을 ID 를 사용해서 넣어주는게 좋을 거 같다

 

추가로 Session User 클래스에 들어가는 필드는 내가 지정해줄 수 있었는데 (지금 프로젝트는 User class 와 똑같은 필드 정보를 가짐 (Auditing 제외) 정보의 일부만 저장하는것도 가능하기 때문에 JPA 가 관리해주지 않는다 

하지만 ID 만 넘겨주면 JPA 가 관리하는 DB 에서 가져오기 때문에 (@Entity 를 달았음) 늘 최신 + 모든 정보니까 ID 만 넘겨주기 

 

결론

 SessionUser 의 ID 만 보내기 

근데 그냥 SessionUser Entity 에 ID 만 저장해도 될 것 같다 수정해주기

@Getter
public class SessionUser {
    private final Long id;

    public SessionUser(Long id) {
        this.id = id;
    }
}

문제5

수정할때 로그인된 사용자가 쓴 글이 맞는지 확인을 해야하는데

그럼 사용자 ID 와 SESSION USER 정보만 있어서 글 내부까진 접근이 안 되는데 그럼 service 로 체크하는걸 미뤄도 되나?

 

해결과정

service 로 미루는게 맞다. 왜냐하면 궁금증에서도 알 수 있다시피 Schedule 엔티티에 접근을 해야하는데 Controller에서는 엔티티를 직접 사용하면 안 됨

 

작성자 검증은 비즈니스 규칙과 조금 더 가까우니까 service 계층에서 처리해주기

글의 작성자와 현재 로그인한 사용자를 비교하려면 엔티티 조회가 필요하고 이런 과정이 비즈니스 로직 

 

결론

Service 로 미루는게 맞다

오히려 Controller 에서 처리하는게 역할이 섞이는 개념 


문제6

로그아웃 기능을 user Controller 에 만들어줘야하나


해결과정

왜 이런 질문을 적었는지 기억이 안나는데 다시 보니까 어차피 세션만료가 자동으로 되지 않는가? 에 대한 고민이었던 것 같다.

마침 튜터님께서 세션 만료에 관한 설명을 해주셔서 간단히 요약하자면

 

세션은 시간이 지나서 만료가 되거나, 우리가 메모리에서 강제로 삭제시킬 수 있음

이 때 시간이 지나서 만료가 되는건 톰캣세팅이나, application.properties 에서 할 수 있고

강제 삭제는 로그아웃 기능을 userController 에 직접 작성해주면 된다.

# 1. 시간이 지나 자동으로 세션 만료, 메모리에서 삭제됨

# login 기능 (in controller)
session.setMaxInactiveInterval(120); # 이 때 매개변수의 단위는 (s)
# 2. 직접 로그아웃 (강제 세션삭제)
@DeleteMapping("/logout")
public ResponseEntity<String> logout(HttpSession session){
    SessionUser sessionUser = (SessionUser) session.getAttribute("loginUser");
    if( sessionUser == null){
        throw new BeforeLoginUserException();
    }

    session.invalidate();
    return ResponseEntity.status(HttpStatus.NO_CONTENT).body("로그아웃 되었습니다.");
}

근데 이렇게 작성하니까 logout 했을 때 

작성해둔 로그아웃 되었습니다. 가 출력되지 않네요 NO_CONTENT 가 이름만 있는게 아니라 진짜 CONTENT 가 출력이 안 되는 거였나봐요 ... ㅎㅎ 저는 그냥 401 402 처럼 204 표시만 해주는줄 알았습니다 ,,, 

OK 로 수정

정상적으로 출력되네요

 

결론

당연히 만들어주어야함...

 

'SPARTA 과제 > SPRING' 카테고리의 다른 글

숙련) 일정관리앱 트러블슈팅 (3)  (0) 2026.02.12
숙련) 일정관리앱 트러블슈팅 (2)  (0) 2026.02.11
숙련) 일정관리앱 업그레이드 초기설정  (1) 2026.02.10
Schedule README  (0) 2026.02.05
유저 입력 검증  (0) 2026.02.04