Skip to content

Conversation

@junyongleee
Copy link

@junyongleee junyongleee commented Nov 5, 2025

Spring으로 전환하기
볼륨 축소 및 JPA 도입한 코드입니다

Summary by CodeRabbit

  • 새로운 기능

    • 세션 기반 가입·로그인 흐름 도입
    • Q&A 작성·목록·상세 및 답글 등록 기능 제공
    • 회원 목록 조회 및 회원정보 수정 기능 추가
  • 개선 사항

    • 애플리케이션 실행 환경 및 DB 설정 정비(로컬 DB 지원)
    • JSP 뷰·네비게이션 및 폼 흐름 전반 개선, 화면별 템플릿 정리

@coderabbitai
Copy link

coderabbitai bot commented Nov 5, 2025

Walkthrough

프로젝트를 Spring Boot 및 Spring Data JPA 기반으로 전환: Gradle 설정과 IDE 메타데이터가 Spring Boot 중심으로 갱신되고, 수동 Tomcat/ConnectionManager/ContextLoaderListener 삭제, JPA 엔티티·리포지토리·서비스 계층과 Spring MVC 컨트롤러 및 JSP 뷰 재배치·추가가 이루어짐.

Changes

응집 범주 / 파일(s) 변경 요약
빌드 파일
build.gradle
Spring Boot 플러그인(id 'org.springframework.boot' v2.7.17)·dependency-management 추가, 그룹/버전 표기 방식 변경, sourceCompatibility=11 추가, 의존성을 Spring Boot 스타터·JPA·Lombok·톰캣 JSP·DB 드라이버 등으로 재구성, 테스트에 useJUnitPlatform() 적용
IDE 프로젝트 설정
java-webMVC.ipr, java-webMVC.iws
Gradle/IDE 설정 대폭 갱신: annotationProcessing(Lombok) 설정, ExternalStorage/GradleSettings/RunManager 등 컴포넌트 추가, 라이브러리 테이블과 여러 모듈/런 구성 추가·포맷 정규화
애플리케이션 엔트리포인트
src/main/java/jwp/WebServerLauncher.java, src/main/resources/application.properties
@SpringBootApplication으로 Spring Boot 진입점 추가(정상적으로 SpringApplication.run 호출), application.properties에 서버 포트·JPA/H2·뷰 리졸버 설정 추가
제거된 수동 서버/연결 코드
src/main/java/WebServerLauncher.java(이전 Tomcat 론처 삭제), src/main/java/core/jdbc/ConnectionManager.java, src/main/java/jwp/support/context/ContextLoaderListener.java
수동 Tomcat 시작기(이전 버전), 수동 JDBC ConnectionManager 및 ServletContextListener 삭제
JPA 엔티티
src/main/java/jwp/model/User.java, src/main/java/jwp/model/Question.java
@Entity, @Table, @Id 등 JPA 매핑 추가, Lombok @NoArgsConstructor(access = PROTECTED) 적용, Question에는 @PrePersist 초기화 로직 추가
리포지토리
src/main/java/jwp/repository/QuestionRepository.java, src/main/java/jwp/repository/UserRepository.java
Spring Data JPA JpaRepository 기반 리포지토리 인터페이스 추가
서비스 계층
src/main/java/jwp/service/UserService.java, src/main/java/jwp/service/QuestionService.java
@Service, @RequiredArgsConstructor, 트랜잭션 경계(@Transactional) 적용된 CRUD·인증·생성·조회 로직 추가
컨트롤러(활성 코드)
src/main/java/jwp/controller/HomeController.java, src/main/java/jwp/controller/UserController.java, src/main/java/jwp/controller/QuestionController.java, src/main/java/jwp/controller/DispatcherServlet.java, src/main/java/jwp/controller/RequestMapping.java, src/main/java/jwp/controller/ShowQuestionController.java(일부는 주석/비활성 포함)
Servlet 기반에서 Spring MVC로 전환한 컨트롤러들 추가/변환(@Controller, @GetMapping/@PostMapping), 세션 검증(UserSessionUtils) 및 서비스 위임 흐름 구현. 일부 컨트롤러 파일은 주석 처리된 스켈레톤도 포함
컨트롤러(주석·스켈레톤 파일)
src/main/java/jwp/controller/*.java (예: CreateQuestionController.java, CreateQuestionFormController.java, CreateUserController.java, LoginController.java, LogoutController.java, UpdateUserController.java, UpdateUserFormController.java, ForwardController.java, ListUserController.java, Controller.java)
다수의 컨트롤러/핸들러 파일이 추가되었으나 상당량이 주석 처리된 상태로 스켈레톤 또는 레거시 코드가 남아 있음
세션 유틸리티
src/main/java/jwp/support/session/UserSessionUtils.java
세션 내 사용자 읽기·로그인 체크 유틸 추가 (USER_SESSION_KEY, isLogined, getUserFromSession`)
JSP 뷰(신규/이동)
src/main/webapp/home.jsp, src/main/webapp/qna/form.jsp, src/main/webapp/qna/show.jsp, src/main/webapp/include/navigation.jspf
새로운/이동된 JSP 뷰 및 네비게이션 프래그먼트 추가 — JSTL 사용 및 경로/링크 조정
루트 뷰 삭제
webapp/home.jsp, webapp/qna/form.jsp, webapp/qna/show.jsp
기존 프로젝트 루트의 JSP 뷰 파일들 제거
정적/리소스 변경
src/main/webapp/...
JSP에 Bootstrap 및 로컬 스크립트 포함, 뷰 경로·포맷 갱신
기타
여러 파일의 주석화·포맷 변경
많은 컨트롤러 파일에 댓글 처리된 코드, 일부 중복 스켈레톤 및 포맷 정리 반영

Sequence Diagram(s)

sequenceDiagram
    actor User
    rect rgba(200,150,200,0.12)
    Note over User: 기존 수동 서블릿 + JDBC 흐름
    User->>Tomcat: HTTP 요청
    Tomcat->>Servlet: doGet()/doPost()
    Servlet->>ConnectionManager: getConnection()
    ConnectionManager->>Database: JDBC
    Database-->>Servlet: ResultSet
    Servlet->>RequestDispatcher: forward to JSP
    RequestDispatcher->>JSP: 렌더링
    JSP-->>User: 응답
    end

    rect rgba(150,200,150,0.12)
    Note over User: Spring Boot + Spring MVC + JPA 흐름
    User->>Spring DispatcherServlet: HTTP 요청
    DispatcherServlet->>Controller: handler (e.g., HomeController)
    Controller->>Service: 비즈니스 로직 호출
    Service->>Repository: JPA 호출
    Repository->>Hibernate: 엔티티 작업 -> Database
    Database-->>Repository: 결과
    Controller->>ViewResolver: view name ("home")
    ViewResolver->>JSP: 렌더링
    JSP-->>User: 응답
    end
Loading
sequenceDiagram
    actor User
    User->>UserController: POST /user/login (id, pw)
    UserController->>UserService: authenticate(id, pw)
    UserService->>UserRepository: findById(id)
    UserRepository->>Database: SELECT
    Database-->>UserRepository: User 엔티티
    alt 인증 성공
        UserService-->>UserController: Optional<User>
        UserController->>HttpSession: setAttribute("user", user)
        UserController-->>User: redirect /
    else 인증 실패
        UserController-->>User: redirect /user/loginFailed
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 분

  • 추가로 주의할 파일/영역:
    • jwp/model/*의 JPA 매핑(컬럼/테이블 이름, 생성 시점 필드 초기화)
    • UserService.updateUser()의 영속성·예외 흐름 및 동시성 고려
    • 컨트롤러의 세션 검사(UserSessionUtils) 일관성 및 리다이렉트 경로
    • build.gradle 의존성 전환으로 인한 JSP 엔진(tomcat-embed-jasper), JSTL, JDBC 드라이버 호환성
    • DispatcherServlet / RequestMapping 추가로 인한 라우팅 중복 또는 기존 서블릿과의 충돌
    • 주석 처리된 컨트롤러 파일(실제 활성 코드와의 중복/혼동 가능성)

Poem

🐰
오래된 커넥션은 놓아주고 나는 폴더를 파네
엔티티 씨앗 심어 서비스가 자라나네
컨트롤러가 길을 열고 리포지토리가 땅을 일구네
부트가 종을 울려 새 시작을 알리네 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive PR 제목은 주차 정보와 팀 및 작성자명을 포함하지만, 핵심 변경사항(Spring 전환, JPA 도입)을 명확히 요약하지 못합니다. Spring Framework 도입 및 JPA 적용, 또는 서블릿에서 Spring MVC로의 마이그레이션 등 기술적 변경사항을 더 명확히 반영한 제목으로 수정을 권장합니다.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
build.gradle (1)

1-40: ---

Spring Boot 버전 업그레이드 필요

현재 build.gradle의 Spring Boot 2.7.17은 최신 버전이 아닙니다. Spring Boot 2.7.18이 2023년 11월 23일에 릴리스되었으므로, 최소한 이 버전으로 업그레이드해야 합니다. 또한 Spring Boot 3.5.7이 2025년 10월 23일에 릴리스되었으므로, 새로운 기능 및 보안 업데이트를 원한다면 Spring Boot 3.x로의 마이그레이션을 고려하세요.

Line 28의 mysql:mysql-connector-java 의존성에 버전을 명시하세요. 현재 버전이 지정되지 않으면 빌드 시 예측 불가능한 버전이 사용될 수 있습니다.

권장사항:

  • Spring Boot를 2.7.18로 업그레이드하거나 3.5.7로 마이그레이션
  • MySQL 커넥터 버전 명시 (예: mysql:mysql-connector-java:8.0.33)
🧹 Nitpick comments (20)
src/main/java/core/jdbc/RowMapper.java (1)

1-12: 주석 처리된 스텁 파일을 정리해주세요.
Line [1]부터 Line [12]까지 전부 주석으로만 남아 있어 실제로는 어떤 기능도 제공하지 않습니다. 필요 없다면 파일 자체를 제거하거나, 사용 계획이 확실하다면 주석을 풀어 실제 인터페이스로 반영해 주세요. 현 상태는 향후 혼란만 키울 수 있습니다.

src/main/webapp/qna/show.jsp (1)

13-130: 주석 처리된 예제 코드를 제거하세요.

JSP 파일에 대량의 주석 처리된 예제 코드가 포함되어 있습니다. 실제 구현(lines 131-168)이 완료되었다면, 이러한 참고용 주석은 제거하여 파일의 가독성을 높이는 것이 좋습니다.

src/main/java/jwp/controller/ForwardController.java (2)

8-8: 주석 처리된 필드를 제거하세요.

사용하지 않는 주석 처리된 필드는 코드의 명확성을 저해합니다. 제거하는 것이 좋습니다.

 @Controller
 public class ForwardController  {
-//    private final String viewName;
     @GetMapping("/user/loginForm")

1-23: 선택사항: 클래스 레벨에 @RequestMapping을 추가할 수 있습니다.

현재 구현은 정상적으로 동작하지만, 관련된 엔드포인트들을 그룹화하기 위해 클래스 레벨에 @RequestMapping("/user")를 추가하고 메서드 레벨의 경로를 단순화하는 것을 고려할 수 있습니다. 다만 현재처럼 다양한 경로를 처리하는 경우에는 현재 방식이 더 명확할 수 있습니다.

java-webMVC.ipr (1)

1-1007: IDE 프로젝트 파일 관리 전략을 검토하세요.

.ipr 파일은 IntelliJ IDEA의 프로젝트 설정 파일입니다. 이 파일에는 Lombok 설정, Gradle 통합 구성 등 유용한 정보가 포함되어 있지만, 대규모 라이브러리 테이블(lines 239-964)과 같은 자동 생성된 내용도 많이 포함되어 있습니다.

최신 IntelliJ IDEA 프로젝트는 .idea 디렉토리 기반 구조를 사용하며, .ipr 파일 대신 다음을 권장합니다:

  • .idea 디렉토리 사용
  • .idea/workspace.xml과 같은 개인 설정 파일은 .gitignore에 추가
  • 프로젝트 공통 설정만 버전 관리에 포함
src/main/java/core/jdbc/KeyHolder.java (1)

1-13: 주석 처리된 코드 정리를 고려하세요.

Spring Boot와 JPA로 마이그레이션 중이므로 이 JDBC 유틸리티 스켈레톤 코드가 주석 처리된 것은 이해할 수 있습니다. 그러나 더 이상 사용하지 않을 코드라면 향후 유지보수성을 위해 완전히 제거하는 것을 권장합니다.

src/main/java/core/jdbc/ConnectionManager.java (1)

1-34: 주석 처리된 레거시 코드 제거를 권장합니다.

Spring Boot가 DataSource를 자동으로 구성하므로 이 수동 연결 관리자는 더 이상 필요하지 않습니다. 마이그레이션이 완료되면 주석 처리된 코드를 삭제하여 코드베이스를 깔끔하게 유지하세요.

src/main/java/jwp/controller/CreateUserController.java (1)

17-21: 입력 유효성 검사 추가를 권장합니다.

사용자 입력을 검증하지 않고 바로 서비스로 전달하고 있습니다. @ValidBindingResult를 사용하거나 서비스 계층에서 유효성 검사를 수행하여 잘못된 데이터가 저장되지 않도록 하세요.

다음과 같이 개선할 수 있습니다:

+import javax.validation.Valid;
+import org.springframework.validation.BindingResult;

     @PostMapping("/signup")
-    public String createUser(@ModelAttribute User user) {
+    public String createUser(@Valid @ModelAttribute User user, BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            return "user/form";
+        }
         userService.createUser(user);
         return "redirect:/user/list";
     }
src/main/java/jwp/controller/RequestMapping.java (1)

1-28: 주석 처리된 파일을 제거하세요.

전체 파일이 주석 처리되어 있으며, Spring MVC 마이그레이션 후 더 이상 사용되지 않는 레거시 코드입니다. 주석 처리된 코드는 유지보수성을 저하시키므로 파일 전체를 삭제하는 것이 좋습니다. 필요시 Git 히스토리를 통해 확인할 수 있습니다.

src/main/webapp/qna/form.jsp (1)

12-35: 사용되지 않는 주석 코드를 제거하세요.

12-35번 라인의 주석 처리된 코드는 회원가입 폼 관련 코드로 보이며, 질문 작성 폼과 관련이 없습니다. 코드 가독성을 위해 제거하는 것을 권장합니다.

src/main/java/jwp/controller/UserController.java (1)

24-27: 일관성을 위해 UserSessionUtils를 사용하세요.

다른 컨트롤러들(예: UpdateUserController, CreateQuestionController)은 UserSessionUtils.getUserFromSession() 또는 isLogined()를 사용하여 세션을 검증합니다. 일관성을 위해 동일한 패턴을 사용하는 것을 권장합니다.

다음과 같이 수정할 수 있습니다:

-    HttpSession session = req.getSession(false);
-    if (session == null) {
+    if (!UserSessionUtils.isLogined(req.getSession(false))) {
         return "redirect:/user/login";
     }
src/main/webapp/home.jsp (1)

19-54: 주석 처리된 예제 코드를 제거하세요.

19-54번 라인의 주석 처리된 코드는 예제나 템플릿 코드로 보입니다. 실제 기능과 무관하므로 제거하여 가독성을 개선하는 것을 권장합니다.

src/main/java/jwp/controller/CreateQuestionController.java (1)

4-4: 사용되지 않는 import를 제거하세요.

다음 import들은 코드에서 사용되지 않으므로 제거하는 것이 좋습니다:

  • Line 4: jwp.model.Question
  • Lines 14-19: ServletException, HttpServletRequest, HttpServletResponse, IOException, SQLException

이들은 서블릿 기반 코드에서 남은 것으로 보이며, Spring MVC에서는 필요하지 않습니다.

Also applies to: 14-19

src/main/java/jwp/dao/QuestionDao.java (1)

1-62: 주석 처리된 DAO 파일을 제거하세요.

전체 파일이 주석 처리되어 있으며, JPA 마이그레이션 후 JDBC 기반의 DAO는 더 이상 필요하지 않습니다. 주석 처리된 코드는 코드베이스를 복잡하게 만들므로, 파일을 삭제하는 것을 권장합니다. 필요시 Git 히스토리를 통해 참조할 수 있습니다.

src/main/java/core/jdbc/JdbcTemplate.java (1)

1-72: 주석 처리된 템플릿 클래스 정리 권장

파일 전체가 주석 처리된 상태로 남아 있어 의미 없는 코드가 커밋에 포함되었습니다. Git 이력이 있으므로 불필요한 주석 블록은 제거하거나, 실제로 사용할 구현을 복원해 주세요.

src/main/java/jwp/service/UserService.java (2)

18-21: 중복 사용자 검증 추가 고려

userId가 이미 존재하는 경우에 대한 검증이 없어 데이터베이스 제약 조건 위반이 발생할 수 있습니다. 사용자 생성 전에 중복 체크를 추가하는 것을 권장합니다.

다음 diff를 적용하여 중복 검증을 추가할 수 있습니다:

 @Transactional
 public User createUser(User user) {
+    if (userRepository.findById(user.getUserId()).isPresent()) {
+        throw new IllegalArgumentException("User already exists: " + user.getUserId());
+    }
     return userRepository.save(user);
 }

23-25: 페이지네이션 추가 고려

사용자 수가 많아질 경우 모든 사용자를 한 번에 조회하면 성능 문제가 발생할 수 있습니다. 향후 페이지네이션을 고려해보세요.

src/main/java/jwp/model/Question.java (2)

25-28: 불필요한 @column name 속성 제거 가능

필드명과 컬럼명이 동일할 때는 @Column(name = "questionId") 속성을 생략할 수 있습니다.

다음과 같이 간소화할 수 있습니다:

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
-@Column(name = "questionId")
 private Long questionId;

58-66: LGTM! 기본값 초기화가 적절합니다

@PrePersist를 사용하여 엔티티 저장 전에 기본값을 설정하는 것은 좋은 패턴입니다. 다만 Line 42의 countOfAnswer 컬럼이 nullable로 정의되어 있지만 실제로는 항상 0으로 초기화되므로, 향후 스키마에서 nullable = false로 변경하는 것을 고려해보세요.

src/main/java/jwp/controller/HomeController.java (1)

10-19: LGTM! 깔끔한 Spring MVC 전환

Servlet에서 Spring MVC로의 마이그레이션이 올바르게 이루어졌습니다. 의존성 주입, 요청 매핑, 모델 처리가 모두 Spring MVC 컨벤션을 잘 따르고 있습니다.

참고: Line 9의 불필요한 빈 줄은 제거하는 것이 일관성에 좋습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a27f59 and 42fac50.

⛔ Files ignored due to path filters (3)
  • src/main/webapp/favicon.ico is excluded by !**/*.ico
  • src/main/webapp/img/KUIT.png is excluded by !**/*.png
  • src/main/webapp/img/picture.jpeg is excluded by !**/*.jpeg
📒 Files selected for processing (42)
  • build.gradle (1 hunks)
  • java-webMVC.ipr (3 hunks)
  • java-webMVC.iws (1 hunks)
  • src/main/java/WebServerLauncher.java (1 hunks)
  • src/main/java/core/jdbc/ConnectionManager.java (1 hunks)
  • src/main/java/core/jdbc/JdbcTemplate.java (1 hunks)
  • src/main/java/core/jdbc/KeyHolder.java (1 hunks)
  • src/main/java/core/jdbc/PreparedStatementSetter.java (1 hunks)
  • src/main/java/core/jdbc/RowMapper.java (1 hunks)
  • src/main/java/jwp/controller/Controller.java (1 hunks)
  • src/main/java/jwp/controller/CreateQuestionController.java (1 hunks)
  • src/main/java/jwp/controller/CreateQuestionFormController.java (1 hunks)
  • src/main/java/jwp/controller/CreateUserController.java (1 hunks)
  • src/main/java/jwp/controller/DispatcherServlet.java (1 hunks)
  • src/main/java/jwp/controller/ForwardController.java (1 hunks)
  • src/main/java/jwp/controller/HomeController.java (1 hunks)
  • src/main/java/jwp/controller/ListUserController.java (1 hunks)
  • src/main/java/jwp/controller/LoginController.java (1 hunks)
  • src/main/java/jwp/controller/LogoutController.java (1 hunks)
  • src/main/java/jwp/controller/RequestMapping.java (1 hunks)
  • src/main/java/jwp/controller/ShowQuestionController.java (1 hunks)
  • src/main/java/jwp/controller/UpdateUserController.java (1 hunks)
  • src/main/java/jwp/controller/UpdateUserFormController.java (1 hunks)
  • src/main/java/jwp/controller/UserController.java (1 hunks)
  • src/main/java/jwp/dao/QuestionDao.java (1 hunks)
  • src/main/java/jwp/dao/UserDao.java (1 hunks)
  • src/main/java/jwp/model/Question.java (1 hunks)
  • src/main/java/jwp/model/User.java (1 hunks)
  • src/main/java/jwp/repository/QuestionRepository.java (1 hunks)
  • src/main/java/jwp/repository/UserRepository.java (1 hunks)
  • src/main/java/jwp/service/QuestionService.java (1 hunks)
  • src/main/java/jwp/service/UserService.java (1 hunks)
  • src/main/java/jwp/support/context/ContextLoaderListener.java (1 hunks)
  • src/main/java/jwp/support/session/UserSessionUtils.java (1 hunks)
  • src/main/resources/application.properties (1 hunks)
  • src/main/webapp/home.jsp (1 hunks)
  • src/main/webapp/include/navigation.jspf (1 hunks)
  • src/main/webapp/qna/form.jsp (1 hunks)
  • src/main/webapp/qna/show.jsp (1 hunks)
  • webapp/home.jsp (0 hunks)
  • webapp/qna/form.jsp (0 hunks)
  • webapp/qna/show.jsp (0 hunks)
💤 Files with no reviewable changes (3)
  • webapp/qna/form.jsp
  • webapp/home.jsp
  • webapp/qna/show.jsp
🧰 Additional context used
🧬 Code graph analysis (15)
src/main/java/jwp/controller/UpdateUserController.java (3)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/UpdateUserFormController.java (1)
  • Controller (15-34)
src/main/java/jwp/controller/CreateQuestionFormController.java (4)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/CreateQuestionController.java (1)
  • Controller (21-37)
src/main/java/jwp/controller/ForwardController.java (1)
  • Controller (6-23)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/LoginController.java (3)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/ForwardController.java (1)
  • Controller (6-23)
src/main/java/jwp/controller/LogoutController.java (1)
  • Controller (10-21)
src/main/java/jwp/controller/UserController.java (3)
src/main/java/jwp/controller/CreateUserController.java (1)
  • Controller (11-22)
src/main/java/jwp/controller/ForwardController.java (1)
  • Controller (6-23)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/ShowQuestionController.java (3)
src/main/java/jwp/controller/CreateQuestionController.java (1)
  • Controller (21-37)
src/main/java/jwp/controller/HomeController.java (1)
  • Controller (10-19)
src/main/java/jwp/controller/UpdateUserFormController.java (1)
  • Controller (15-34)
src/main/java/jwp/controller/UpdateUserFormController.java (3)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/UpdateUserController.java (1)
  • Controller (15-30)
src/main/java/jwp/controller/CreateQuestionController.java (2)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/ForwardController.java (2)
src/main/java/jwp/controller/CreateQuestionFormController.java (1)
  • Controller (10-21)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/HomeController.java (2)
src/main/java/jwp/controller/CreateQuestionController.java (1)
  • Controller (21-37)
src/main/java/jwp/controller/ShowQuestionController.java (1)
  • Controller (11-26)
src/main/java/jwp/service/QuestionService.java (1)
src/main/java/jwp/service/UserService.java (1)
  • Service (12-43)
src/main/java/jwp/controller/CreateUserController.java (4)
src/main/java/jwp/controller/ForwardController.java (1)
  • Controller (6-23)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/UpdateUserController.java (1)
  • Controller (15-30)
src/main/java/jwp/controller/UserController.java (1)
  • Controller (12-32)
src/main/java/jwp/model/Question.java (1)
src/main/java/jwp/model/User.java (1)
  • Entity (10-106)
src/main/java/jwp/model/User.java (1)
src/main/java/jwp/model/Question.java (1)
  • Entity (14-91)
src/main/java/jwp/service/UserService.java (1)
src/main/java/jwp/service/QuestionService.java (1)
  • Service (13-32)
src/main/java/jwp/controller/LogoutController.java (3)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/UpdateUserController.java (1)
  • Controller (15-30)
🔇 Additional comments (15)
src/main/webapp/qna/show.jsp (1)

20-20: 이미지 경로를 확인하세요.

프로필 이미지 경로가 ../img/picture.jpeg로 하드코딩되어 있습니다. 실제 프로젝트에서는 사용자별 프로필 이미지를 동적으로 로드하거나, 기본 이미지의 경로가 올바른지 확인이 필요합니다.

Also applies to: 136-136

src/main/java/jwp/support/session/UserSessionUtils.java (1)

1-27: LGTM! 잘 설계된 세션 유틸리티 클래스입니다.

이 유틸리티 클래스는 다음과 같은 장점을 가지고 있습니다:

  • 세션 키 상수를 중앙화하여 일관성 보장
  • null 안전성을 고려한 안전한 구현
  • instanceof를 사용한 타입 안전 캐스팅
  • 적절한 캡슐화 (private 생성자)

여러 컨트롤러에서 일관되게 사용할 수 있는 좋은 추상화입니다.

src/main/java/jwp/repository/UserRepository.java (1)

1-7: LGTM!

Spring Data JPA 컨벤션을 잘 따르고 있으며, 기본 CRUD 작업은 JpaRepository가 제공합니다. 구현이 깔끔합니다.

src/main/java/jwp/repository/QuestionRepository.java (1)

1-7: LGTM!

Spring Data JPA 리포지토리가 올바르게 구성되었습니다. 기본 CRUD 작업이 자동으로 제공됩니다.

src/main/resources/application.properties (2)

10-10: 프로덕션 환경에서 SQL 초기화 모드를 확인하세요.

spring.sql.init.mode=always는 애플리케이션이 시작될 때마다 SQL 스크립트를 실행합니다. 개발 환경에서는 적합하지만, 프로덕션 환경에서는 데이터 손실이나 중복 실행 문제를 일으킬 수 있습니다. 프로덕션에서는 never 또는 embedded로 설정하는 것을 권장합니다.


1-14: 설정이 적절합니다.

개발 환경을 위한 Spring Boot 설정이 잘 구성되어 있습니다. H2 데이터베이스, JPA, 그리고 JSP 뷰 리졸버 설정이 올바릅니다.

src/main/java/jwp/controller/ShowQuestionController.java (1)

17-25: LGTM!

컨트롤러 구현이 깔끔하고 올바릅니다. Optional 처리를 통해 존재하지 않는 질문에 대한 예외 처리가 잘 되어 있으며, 뷰 이름도 정확합니다.

src/main/java/jwp/controller/LogoutController.java (1)

14-20: 안전한 로그아웃 구현입니다.

세션 존재 여부를 확인한 후 속성을 제거하는 로직이 올바르게 구현되어 있습니다. Spring MVC에서 세션이 없을 경우 session 파라미터가 null이 될 수 있으므로 null 체크가 적절합니다.

src/main/java/jwp/controller/CreateQuestionController.java (1)

26-36: 질문 생성 로직이 올바르게 구현되었습니다.

세션에서 사용자를 확인하고, 인증되지 않은 경우 로그인 폼으로 리다이렉트하는 로직이 적절합니다. 다른 컨트롤러들과 일관된 패턴을 따르고 있습니다.

src/main/java/jwp/controller/UpdateUserController.java (1)

20-29: 사용자 업데이트 로직이 안전하게 구현되었습니다.

세션의 사용자와 업데이트 대상 사용자를 비교하여 본인만 정보를 수정할 수 있도록 검증하는 로직이 적절합니다. 업데이트 후 세션 정보를 갱신하는 것도 올바른 패턴입니다.

src/main/webapp/qna/form.jsp (1)

41-42: 세션 검증 로직이 올바르게 구현되어 있으므로, 제시된 우려사항은 근거가 없습니다.

CreateQuestionFormController.showForm() 메서드에서 로그인 여부를 먼저 검증합니다. 사용자가 로그인하지 않은 경우 /user/loginForm으로 리다이렉트되므로, form.jsp는 오직 인증된 사용자만 접근할 수 있습니다. 따라서 ${sessionScope.user.userId}의 null 참조 오류는 발생할 수 없습니다.

Likely an incorrect or invalid review comment.

src/main/java/jwp/service/UserService.java (3)

27-29: LGTM!

Optional을 적절히 사용하여 사용자 조회를 처리하고 있습니다.


31-37: LGTM!

JPA dirty checking을 활용한 올바른 업데이트 패턴입니다. 영속 상태의 엔티티를 조회하고 변경하면 트랜잭션 종료 시 자동으로 업데이트 쿼리가 실행됩니다.


39-42: LGTM!

Optional의 filter를 활용한 깔끔한 인증 로직입니다. 비밀번호 검증을 User 엔티티에 위임하여 책임을 적절히 분리했습니다.

src/main/java/jwp/model/Question.java (1)

45-56: LGTM!

생성자 설계가 적절합니다. 간단한 생성자는 새로운 질문 생성에, 전체 생성자는 모든 필드를 포함한 재구성에 사용할 수 있습니다.

implementation("org.apache.commons:commons-dbcp2:2.6.0")
// 4. 데이터베이스 드라이버 (H2, MySQL 등 필요한 것 사용)
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

MySQL 커넥터 의존성 버전을 명시하세요.

MySQL 커넥터에 버전이 명시되어 있지 않습니다. Spring Boot의 의존성 관리가 처리할 수 있지만, mysql-connector-java는 deprecated되었으며 mysql-connector-j로 대체되었습니다.

다음과 같이 수정하는 것을 권장합니다:

-    runtimeOnly 'mysql:mysql-connector-java'
+    runtimeOnly 'com.mysql:mysql-connector-j'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
runtimeOnly 'mysql:mysql-connector-java'
runtimeOnly 'com.mysql:mysql-connector-j'
🤖 Prompt for AI Agents
In build.gradle around line 28, the runtimeOnly dependency uses the deprecated
artifact 'mysql:mysql-connector-java' without an explicit version; replace it
with the current artifact 'mysql:mysql-connector-j' and specify an explicit
version (or import/use the Spring Boot dependency management BOM to resolve a
compatible version) to avoid relying on deprecated coordinates and ensure
repeatable builds; update the dependency declaration to use
'mysql:mysql-connector-j:YOUR_CHOSEN_VERSION' or remove the version if you add
the Boot BOM and document which Boot version provides the connector.

Comment on lines 1 to 349
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<option name="TRACKING_ENABLED" value="true"/>
<option name="SHOW_DIALOG" value="false"/>
<option name="HIGHLIGHT_CONFLICTS" value="true"/>
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false"/>
<option name="LAST_RESOLUTION" value="IGNORE"/>
<list default="true" id="b8532e5e-d8f5-4e8c-a002-746704734daf" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/src/main/java/jwp/repository/QuestionRepository.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/jwp/repository/UserRepository.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/jwp/service/QuestionService.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/jwp/service/UserService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/java-webMVC.iws" beforeDir="false" afterPath="$PROJECT_DIR$/java-webMVC.iws" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/core/jdbc/ConnectionManager.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/jdbc/ConnectionManager.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/core/jdbc/KeyHolder.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/jdbc/KeyHolder.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/core/jdbc/PreparedStatementSetter.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/jdbc/PreparedStatementSetter.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/core/jdbc/RowMapper.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/core/jdbc/RowMapper.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/Controller.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/Controller.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/CreateQuestionController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/CreateQuestionController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/CreateQuestionFormController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/CreateQuestionFormController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/CreateUserController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/CreateUserController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/DispatcherServlet.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/DispatcherServlet.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/ForwardController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/ForwardController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/HomeController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/HomeController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/ListUserController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/ListUserController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/LoginController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/LoginController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/LogoutController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/LogoutController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/RequestMapping.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/RequestMapping.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/ShowQuestionController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/ShowQuestionController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/UpdateUserController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/UpdateUserController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/UpdateUserFormController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/UpdateUserFormController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/controller/UserController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/controller/UserController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/dao/QuestionDao.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/dao/QuestionDao.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/dao/UserDao.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/dao/UserDao.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/model/Question.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/model/Question.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/model/User.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/model/User.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/jwp/support/context/ContextLoaderListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/jwp/support/context/ContextLoaderListener.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ChangesViewManager" flattened_view="true" show_ignored="false"/>
<component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value=""/>
<option name="REVERSE_PATCH" value="false"/>
<option name="PATCH_PATH" value="" />
<option name="REVERSE_PATCH" value="false" />
</component>
<component name="DaemonCodeAnalyzer">
<disable_hints/>
<disable_hints />
</component>
<component name="DebuggerManager">
<breakpoint_any>
<breakpoint_any converted="true">
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true"/>
<option name="NOTIFY_UNCAUGHT" value="true"/>
<option name="ENABLED" value="false"/>
<option name="LOG_ENABLED" value="false"/>
<option name="LOG_EXPRESSION_ENABLED" value="false"/>
<option name="SUSPEND_POLICY" value="SuspendAll"/>
<option name="COUNT_FILTER_ENABLED" value="false"/>
<option name="COUNT_FILTER" value="0"/>
<option name="CONDITION_ENABLED" value="false"/>
<option name="CLASS_FILTERS_ENABLED" value="false"/>
<option name="INSTANCE_FILTERS_ENABLED" value="false"/>
<option name="CONDITION" value=""/>
<option name="LOG_MESSAGE" value=""/>
<option name="NOTIFY_CAUGHT" value="true" />
<option name="NOTIFY_UNCAUGHT" value="true" />
<option name="ENABLED" value="false" />
<option name="LOG_ENABLED" value="false" />
<option name="LOG_EXPRESSION_ENABLED" value="false" />
<option name="SUSPEND_POLICY" value="SuspendAll" />
<option name="COUNT_FILTER_ENABLED" value="false" />
<option name="COUNT_FILTER" value="0" />
<option name="CONDITION_ENABLED" value="false" />
<option name="CLASS_FILTERS_ENABLED" value="false" />
<option name="INSTANCE_FILTERS_ENABLED" value="false" />
<option name="CONDITION" value="" />
<option name="LOG_MESSAGE" value="" />
</breakpoint>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true"/>
<option name="NOTIFY_UNCAUGHT" value="true"/>
<option name="ENABLED" value="false"/>
<option name="LOG_ENABLED" value="false"/>
<option name="LOG_EXPRESSION_ENABLED" value="false"/>
<option name="SUSPEND_POLICY" value="SuspendAll"/>
<option name="COUNT_FILTER_ENABLED" value="false"/>
<option name="COUNT_FILTER" value="0"/>
<option name="CONDITION_ENABLED" value="false"/>
<option name="CLASS_FILTERS_ENABLED" value="false"/>
<option name="INSTANCE_FILTERS_ENABLED" value="false"/>
<option name="CONDITION" value=""/>
<option name="LOG_MESSAGE" value=""/>
<option name="NOTIFY_CAUGHT" value="true" />
<option name="NOTIFY_UNCAUGHT" value="true" />
<option name="ENABLED" value="false" />
<option name="LOG_ENABLED" value="false" />
<option name="LOG_EXPRESSION_ENABLED" value="false" />
<option name="SUSPEND_POLICY" value="SuspendAll" />
<option name="COUNT_FILTER_ENABLED" value="false" />
<option name="COUNT_FILTER" value="0" />
<option name="CONDITION_ENABLED" value="false" />
<option name="CLASS_FILTERS_ENABLED" value="false" />
<option name="INSTANCE_FILTERS_ENABLED" value="false" />
<option name="CONDITION" value="" />
<option name="LOG_MESSAGE" value="" />
</breakpoint>
</breakpoint_any>
<breakpoint_rules/>
<ui_properties/>
<breakpoint_rules converted="true" />
<ui_properties converted="true" />
</component>
<component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$">
<ProjectState />
</projectState>
</component>
<component name="ExternalProjectsManager">
<system id="GRADLE">
<state>
<task path="$PROJECT_DIR$">
<activation />
</task>
<projects_view>
<tree_state>
<expand>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="java-webMVC" type="f1a62948:ProjectNode" />
</path>
</expand>
<select />
</tree_state>
</projects_view>
</state>
</system>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Interface" />
<option value="Class" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="KubernetesApiPersistence">{}</component>
<component name="KubernetesApiProvider">{
&quot;isMigrated&quot;: true
}</component>
<component name="ModuleEditorState">
<option name="LAST_EDITED_MODULE_NAME"/>
<option name="LAST_EDITED_TAB_NAME"/>
<option name="LAST_EDITED_MODULE_NAME" />
<option name="LAST_EDITED_TAB_NAME" />
</component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="ProjectErrors" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 3
}</component>
<component name="ProjectId" id="33rFKd1PbyFgveTj0SfXwnrE54G" />
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state/>
<profile-state />
</entry>
</component>
<component name="ProjectLevelVcsManager">
<OptionsSetting value="true" id="Add"/>
<OptionsSetting value="true" id="Remove"/>
<OptionsSetting value="true" id="Checkout"/>
<OptionsSetting value="true" id="Update"/>
<OptionsSetting value="true" id="Status"/>
<OptionsSetting value="true" id="Edit"/>
<ConfirmationsSetting value="0" id="Add"/>
<ConfirmationsSetting value="0" id="Remove"/>
</component>
<component name="ProjectReloadState">
<option name="STATE" value="0"/>
</component>
<component name="PropertiesComponent">
<property name="GoToFile.includeJavaFiles" value="false"/>
<property name="GoToClass.toSaveIncludeLibraries" value="false"/>
<property name="MemberChooser.sorted" value="false"/>
<property name="MemberChooser.showClasses" value="true"/>
<property name="GoToClass.includeLibraries" value="false"/>
<property name="MemberChooser.copyJavadoc" value="false"/>
</component>
<component name="RunManager">
<configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true"/>
<option name="SERVER_MODE" value="false"/>
<option name="SHMEM_ADDRESS" value="javadebug"/>
<option name="HOST" value="localhost"/>
<option name="PORT" value="5005"/>
<option name="STATE" value="0" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Application.WebServerLauncher.executor": "Run",
"Gradle.KUIT6_Server-webMVC.executor": "Run",
"JUnit.UserTest.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"Spring Boot.WebServerLauncher.executor": "Run",
"git-widget-placeholder": "week06junyong",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "C:/Users/samsung-user/Documents/KU/clubs/KUIT/KUIT6_Server-webMVC",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"project.structure.last.edited": "Project",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.23492062",
"settings.editor.selected.configurable": "reference.settingsdialog.project.gradle",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\samsung-user\Documents\KU\clubs\KUIT\KUIT6_Server-webMVC\src\main" />
</key>
</component>
<component name="RunManager" selected="Spring Boot.WebServerLauncher">
<configuration default="true" type="Applet" factoryName="Applet">
<module name="" />
<option name="MAIN_CLASS_NAME" />
<option name="HTML_FILE_NAME" />
<option name="HTML_USED" value="false" />
<option name="WIDTH" value="400" />
<option name="HEIGHT" value="300" />
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
<option name="VM_PARAMETERS" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" />
<method>
<option name="BuildArtifacts" enabled="false"/>
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="Applet" factoryName="Applet">
<module name=""/>
<option name="MAIN_CLASS_NAME"/>
<option name="HTML_FILE_NAME"/>
<option name="HTML_USED" value="false"/>
<option name="WIDTH" value="400"/>
<option name="HEIGHT" value="300"/>
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy"/>
<option name="VM_PARAMETERS"/>
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false"/>
<option name="ALTERNATIVE_JRE_PATH"/>
<method>
<option name="BuildArtifacts" enabled="false"/>
<option name="Make" enabled="true"/>
<configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
<Host>localhost</Host>
<Port>5050</Port>
</configuration>
<configuration name="WebServerLauncher" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="WebServerLauncher" />
<module name="org.example.java-webMVC.main" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<method v="2">
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="Application" factoryName="Application">
<extension name="coverage" enabled="false" merge="false"/>
<option name="MAIN_CLASS_NAME"/>
<option name="VM_PARAMETERS"/>
<option name="PROGRAM_PARAMETERS"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$"/>
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false"/>
<option name="ALTERNATIVE_JRE_PATH"/>
<option name="ENABLE_SWING_INSPECTOR" value="false"/>
<option name="ENV_VARIABLES"/>
<option name="PASS_PARENT_ENVS" value="true"/>
<module name=""/>
<envs/>
<method>
<option name="BuildArtifacts" enabled="false"/>
<option name="Make" enabled="true"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<method v="2">
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration name="KUIT6_Server-webMVC" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list />
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="UserTest" type="JUnit" factoryName="JUnit" temporary="true" nameIsGenerated="true">
<module name="org.example.java-webMVC.test" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="jwp.model.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="PACKAGE_NAME" value="jwp.model" />
<option name="MAIN_CLASS_NAME" value="jwp.model.UserTest" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="moduleWithDependencies" />
</option>
<method v="2">
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
</method>
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit">
<extension name="coverage" enabled="false" merge="false"/>
<module name=""/>
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false"/>
<option name="ALTERNATIVE_JRE_PATH"/>
<option name="PACKAGE_NAME"/>
<option name="MAIN_CLASS_NAME"/>
<option name="METHOD_NAME"/>
<option name="TEST_OBJECT" value="class"/>
<option name="VM_PARAMETERS"/>
<option name="PARAMETERS"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$"/>
<option name="ENV_VARIABLES"/>
<option name="PASS_PARENT_ENVS" value="true"/>
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="TEST_SEARCH_SCOPE">
<value defaultName="moduleWithDependencies"/>
<value defaultName="moduleWithDependencies" />
</option>
<envs/>
<method>
<option name="BuildArtifacts" enabled="false"/>
<option name="Make" enabled="true"/>
<method v="2">
<option name="BuildArtifacts" enabled="false" />
<option name="Make" enabled="true" />
</method>
</configuration>
<list size="0"/>
<configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
<Host>localhost</Host>
<Port>5050</Port>
<configuration default="true" type="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" value="javadebug" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
<option name="AUTO_RESTART" value="false" />
<method v="2">
<option name="BuildArtifacts" enabled="false" />
</method>
</configuration>
<configuration name="WebServerLauncher" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" temporary="true" nameIsGenerated="true">
<module name="org.example.java-webMVC.main" />
<option name="SPRING_BOOT_MAIN_CLASS" value="WebServerLauncher" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
<list>
<item itemvalue="Application.WebServerLauncher" />
<item itemvalue="Gradle.KUIT6_Server-webMVC" />
<item itemvalue="JUnit.UserTest" />
<item itemvalue="Spring Boot.WebServerLauncher" />
</list>
<recent_temporary>
<list>
<item itemvalue="Spring Boot.WebServerLauncher" />
<item itemvalue="Application.WebServerLauncher" />
<item itemvalue="JUnit.UserTest" />
</list>
</recent_temporary>
</component>
<component name="ShelveChangesManager" show_recycled="false"/>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-9823dce3aa75-28b599e66164-intellij.indexing.shared.core-IU-242.23726.103" />
<option value="bundled-js-predefined-d6986cc7102b-5c90d61e3bab-JavaScript-IU-242.23726.103" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="SvnConfiguration" maxAnnotateRevisions="500">
<option name="USER" value=""/>
<option name="PASSWORD" value=""/>
<option name="LAST_MERGED_REVISION"/>
<option name="UPDATE_RUN_STATUS" value="false"/>
<option name="MERGE_DRY_RUN" value="false"/>
<option name="MERGE_DIFF_USE_ANCESTRY" value="true"/>
<option name="UPDATE_LOCK_ON_DEMAND" value="false"/>
<option name="IGNORE_SPACES_IN_MERGE" value="false"/>
<option name="DETECT_NESTED_COPIES" value="true"/>
<option name="IGNORE_SPACES_IN_ANNOTATE" value="true"/>
<option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true"/>
<option name="USER" value="" />
<option name="PASSWORD" value="" />
<option name="LAST_MERGED_REVISION" />
<option name="UPDATE_RUN_STATUS" value="false" />
<option name="MERGE_DRY_RUN" value="false" />
<option name="MERGE_DIFF_USE_ANCESTRY" value="true" />
<option name="UPDATE_LOCK_ON_DEMAND" value="false" />
<option name="IGNORE_SPACES_IN_MERGE" value="false" />
<option name="DETECT_NESTED_COPIES" value="true" />
<option name="IGNORE_SPACES_IN_ANNOTATE" value="true" />
<option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" />
<myIsUseDefaultProxy>false</myIsUseDefaultProxy>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task"/>
<servers/>
<task active="true" id="Default" summary="Default task">
<changelist id="b8532e5e-d8f5-4e8c-a002-746704734daf" name="Changes" comment="" />
<created>1760067221991</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1760067221991</updated>
<workItem from="1760067222973" duration="9040000" />
<workItem from="1760150551792" duration="2675000" />
<workItem from="1761726540314" duration="3732000" />
<workItem from="1761730317572" duration="1356000" />
<workItem from="1761813440661" duration="22235000" />
<workItem from="1761983761744" duration="329000" />
<workItem from="1762087694007" duration="8947000" />
<workItem from="1762319002804" duration="10575000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VcsManagerConfiguration">
<option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true"/>
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true"/>
<option name="PERFORM_UPDATE_IN_BACKGROUND" value="true"/>
<option name="PERFORM_COMMIT_IN_BACKGROUND" value="true"/>
<option name="PERFORM_EDIT_IN_BACKGROUND" value="true"/>
<option name="PERFORM_CHECKOUT_IN_BACKGROUND" value="true"/>
<option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true"/>
<option name="PERFORM_ROLLBACK_IN_BACKGROUND" value="false"/>
<option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="false"/>
<option name="ENABLE_BACKGROUND_PROCESSES" value="false"/>
<option name="CHANGED_ON_SERVER_INTERVAL" value="60"/>
<option name="FORCE_NON_EMPTY_COMMENT" value="false"/>
<option name="LAST_COMMIT_MESSAGE"/>
<option name="MAKE_NEW_CHANGELIST_ACTIVE" value="true"/>
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false"/>
<option name="CHECK_FILES_UP_TO_DATE_BEFORE_COMMIT" value="false"/>
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false"/>
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false"/>
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8"/>
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5"/>
<option name="ACTIVE_VCS_NAME"/>
<option name="UPDATE_GROUP_BY_PACKAGES" value="false"/>
<option name="UPDATE_GROUP_BY_CHANGELIST" value="false"/>
<option name="SHOW_FILE_HISTORY_AS_TREE" value="false"/>
<option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6"/>
</component>
<component name="XDebuggerManager">
<breakpoint-manager/>
</component>
</project>
<option name="MAKE_NEW_CHANGELIST_ACTIVE" value="true" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />
<select />
</component>
</project>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

IDE 워크스페이스 파일을 버전 관리에서 제외하세요.

.iws 파일은 IntelliJ IDEA의 개인 워크스페이스 설정 파일로, 사용자별 로컬 경로와 개인 설정을 포함합니다 (예: line 158의 C:/Users/samsung-user/...). 이러한 파일은 .gitignore에 추가하여 버전 관리에서 제외하는 것이 일반적인 모범 사례입니다.

.gitignore 파일에 다음을 추가하세요:

*.iws
*.iml
.idea/workspace.xml
.idea/tasks.xml
🤖 Prompt for AI Agents
In java-webMVC.iws (lines 1-349) the IntelliJ workspace file was committed
(contains local paths); stop tracking workspace files by adding the patterns
*.iws, *.iml, .idea/workspace.xml, .idea/tasks.xml to .gitignore, remove the
committed java-webMVC.iws from the repository index so it is no longer tracked
and commit that change, and if you need to purge it from history use a
history-rewrite tool (e.g., git filter-repo or BFG) and force-push after
coordinating with the team.

@GetMapping("/form")
public String showForm(HttpSession session) {
if (UserSessionUtils.isLogined(session)) {
return "qna/form.jsp";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

뷰 이름에서 .jsp 확장자를 제거하세요.

application.properties에서 이미 spring.mvc.view.suffix=.jsp로 설정되어 있으므로, 뷰 이름에 .jsp를 포함하면 최종 경로가 /qna/form.jsp.jsp가 되어 404 오류가 발생합니다.

다음과 같이 수정하세요:

-            return "qna/form.jsp";
+            return "qna/form";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return "qna/form.jsp";
return "qna/form";
🤖 Prompt for AI Agents
In src/main/java/jwp/controller/CreateQuestionFormController.java around line
17, the controller returns a view name that includes the ".jsp" extension which
causes double-suffixing; change the returned view name to omit the ".jsp" suffix
(e.g. return "qna/form") so Spring's configured view suffix is applied
correctly.

Comment on lines 20 to 28
public String login(@RequestParam String userId,
@RequestParam String password,
HttpSession session) {
return userService.authenticate(userId, password)
.map(user -> {
session.setAttribute(UserSessionUtils.USER_SESSION_KEY, user);
return "redirect:/";
})
.orElse("redirect:/user/loginFailed");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

세션 고정 취약점 해결 필요

로그인 성공 후에도 기존 HttpSession ID를 그대로 유지하면 세션 고정(session fixation) 공격에 노출됩니다. 인증 직후 request.changeSessionId()를 호출해 세션 ID를 재발급하도록 수정해 주세요. (위 수정에는 HttpServletRequest 파라미터와 관련 import 추가가 필요합니다.) 또한 향후에는 세션에 저장하는 사용자 정보에서 비밀번호 등 민감 정보를 제거하는 방안도 함께 검토해 주시면 좋겠습니다.

-    public String login(@RequestParam String userId,
-                        @RequestParam String password,
-                        HttpSession session) {
+    public String login(@RequestParam String userId,
+                        @RequestParam String password,
+                        HttpServletRequest request,
+                        HttpSession session) {
         return userService.authenticate(userId, password)
                 .map(user -> {
+                    request.changeSessionId();
                     session.setAttribute(UserSessionUtils.USER_SESSION_KEY, user);
                     return "redirect:/";
                 })
🤖 Prompt for AI Agents
In src/main/java/jwp/controller/LoginController.java around lines 20 to 28, the
controller keeps the existing HttpSession ID after successful authentication
which allows session fixation; update the method signature to accept an
HttpServletRequest parameter (and add the corresponding import), and after
successful authentication call request.changeSessionId() before storing the user
in the session (session.setAttribute...). Ensure you still set the
UserSessionUtils.USER_SESSION_KEY after changeSessionId(), and consider removing
sensitive fields (like password) from the user object stored in session.

Comment on lines +1 to +27
//package jwp.controller;
//
//import jwp.service.UserService;
//import jwp.support.session.UserSessionUtils;
//import lombok.RequiredArgsConstructor;
//import org.springframework.stereotype.Controller;
//import org.springframework.ui.Model;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.RequestMapping;
//
//import javax.servlet.http.HttpSession;
//
//@Controller
//@RequestMapping("/user")
//@RequiredArgsConstructor
//public class UserController {
// private final UserService userService;
//
// @GetMapping("/list")
// public String listUsers(HttpSession session, Model model) {
// if (!UserSessionUtils.isLogined(session)) {
// return "redirect:/user/loginForm";
// }
// model.addAttribute("users", userService.findAllUsers());
// return "user/list";
// }
//}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

주석 처리된 코드를 제거하세요.

이 파일은 전체가 주석 처리되어 있어 실제로 사용되지 않는 코드입니다. 주석 처리된 코드는 코드베이스의 가독성을 저하시키고 혼란을 야기할 수 있으므로, Spring Boot 마이그레이션이 완료된 시점에서는 이 파일을 완전히 삭제하는 것이 좋습니다.

🤖 Prompt for AI Agents
In src/main/java/jwp/support/context/ContextLoaderListener.java lines 1-27 the
entire file contains a commented-out UserController class that is unused and
harms readability; remove this file from the repository (or delete the commented
controller block) so it no longer exists in source, and if you want to preserve
history keep the code in VCS only (do not move it into codebase), also confirm
there are no runtime references before deletion.

<div class="row">
<div class="col-md-5"></div>
<div class="col-md-5">
<ul class="pagination" style="display:align-items-center;">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

잘못된 CSS 구문을 수정하세요.

style="display:align-items-center;"는 유효하지 않은 CSS 구문입니다. displayalign-items를 혼용한 것으로 보입니다.

다음과 같이 수정하세요:

-                <ul class="pagination" style="display:align-items-center;">
+                <ul class="pagination" style="display: flex; align-items: center;">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<ul class="pagination" style="display:align-items-center;">
<ul class="pagination" style="display: flex; align-items: center;">
🤖 Prompt for AI Agents
In src/main/webapp/home.jsp around line 93, the inline style attribute uses an
invalid CSS declaration `display:align-items-center;`; replace it with valid CSS
such as `display:flex; align-items:center;` (or remove the inline style and
apply equivalent classes/CSS) so the pagination UL correctly aligns its items.

</form>
</li>
<li>
<a class="link-modify-article" href="../home.jsp">목록</a>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

하드코딩된 경로를 컨트롤러 경로로 변경하세요.

"목록" 링크가 ../home.jsp로 하드코딩되어 있어 MVC 패턴을 위반합니다. 컨트롤러를 통한 라우팅을 사용하세요.

-                        <a class="link-modify-article" href="../home.jsp">목록</a>
+                        <a class="link-modify-article" href="/">목록</a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a class="link-modify-article" href="../home.jsp">목록</a>
<a class="link-modify-article" href="/">목록</a>
🤖 Prompt for AI Agents
In src/main/webapp/qna/show.jsp around line 53, the "목록" anchor currently
hardcodes ../home.jsp; replace this with a controller route (not a direct JSP
file). Update the href to point to your controller mapping (for example "/home"
or the servlet/controller path) and generate it with the JSP tag library to
include the context path (e.g., use <c:url value="/home"/> or build
"${pageContext.request.contextPath}/yourControllerPath") so routing goes through
the controller rather than linking directly to the JSP.

Comment on lines +169 to +175
<form class="submit-write">
<div class="form-group" style="padding:14px;">
<textarea class="form-control" placeholder="Update your status"></textarea>
</div>
<button class="btn btn-primary pull-right" type="button">답변하기</button>
<div class="clearfix"/>
</form>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

답변 제출 폼의 action과 method를 추가하세요.

답변 제출 폼에 actionmethod 속성이 누락되어 있어 현재 기능하지 않습니다. 서버에 답변을 제출할 수 있도록 적절한 엔드포인트를 설정해야 합니다.

다음과 같이 수정하는 것을 권장합니다:

-                    <form class="submit-write">
+                    <form class="submit-write" action="/questions/${question.questionId}/answers" method="POST">
                         <div class="form-group" style="padding:14px;">
-                            <textarea class="form-control" placeholder="Update your status"></textarea>
+                            <textarea name="contents" class="form-control" placeholder="답변을 입력하세요" required></textarea>
                         </div>
-                        <button class="btn btn-primary pull-right" type="button">답변하기</button>
+                        <button class="btn btn-primary pull-right" type="submit">답변하기</button>
                         <div class="clearfix"/>
                     </form>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<form class="submit-write">
<div class="form-group" style="padding:14px;">
<textarea class="form-control" placeholder="Update your status"></textarea>
</div>
<button class="btn btn-primary pull-right" type="button">답변하기</button>
<div class="clearfix"/>
</form>
<form class="submit-write" action="/questions/${question.questionId}/answers" method="POST">
<div class="form-group" style="padding:14px;">
<textarea name="contents" class="form-control" placeholder="답변을 입력하세요" required></textarea>
</div>
<button class="btn btn-primary pull-right" type="submit">답변하기</button>
<div class="clearfix"/>
</form>
🤖 Prompt for AI Agents
In src/main/webapp/qna/show.jsp around lines 169 to 175 the answer form is
missing action and method attributes (and the submit button is type="button"),
so it won't post to the server; update the <form> to include
action="/qna/{qnaId}/answer" (or the correct endpoint for your app) and
method="post", give the textarea a name like name="content", add any required
hidden inputs (e.g., qnaId, CSRF token) and change the button to type="submit"
so the form actually submits to the server.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (3)
java-webMVC.iws (1)

1-328: IDE 워크스페이스 파일이 계속 커밋되고 있습니다.

이 파일은 이전 리뷰에서 이미 지적되었지만, 여전히 버전 관리에 포함되어 수정되고 있습니다. .iws 파일은 개인별 IDE 설정을 담고 있으며 (예: line 137, 152의 로컬 경로 C:/Users/samsung-user/...), 팀 협업 시 충돌과 불필요한 변경사항을 유발합니다.

.gitignore에 패턴을 추가하고, 이미 추적 중인 이 파일을 인덱스에서 제거해주세요:

# .gitignore에 추가
echo "*.iws" >> .gitignore
echo "*.iml" >> .gitignore
echo ".idea/workspace.xml" >> .gitignore
echo ".idea/tasks.xml" >> .gitignore

# Git 인덱스에서 제거 (파일 자체는 삭제하지 않음)
git rm --cached java-webMVC.iws
git add .gitignore
git commit -m "chore: IDE 워크스페이스 파일 추적 중지"
src/main/java/jwp/controller/UserController.java (2)

36-38: 중복된 HTTP 메서드 체크를 제거하세요.

@GetMapping 어노테이션이 이미 GET 메서드만 허용하므로 수동 메서드 체크는 불필요합니다.

다음과 같이 수정하세요:

 @GetMapping("/list")
 public String listUsers(HttpServletRequest req, Model model) {
-    if (!"GET".equalsIgnoreCase(req.getMethod())) {
-        return "redirect:/user/login";
-    }
-
     HttpSession session = req.getSession(false);

46-46: 뷰 이름에서 .jsp 확장자를 제거하세요.

application.propertiesspring.mvc.view.suffix=.jsp가 설정되어 있으므로 뷰 이름에 .jsp를 포함하면 /user/list.jsp.jsp로 중복됩니다. 다른 메서드들과 일관되게 확장자를 제거하세요.

다음과 같이 수정하세요:

-    return "user/list.jsp";
+    return "user/list";
🧹 Nitpick comments (5)
src/main/java/jwp/controller/QuestionController.java (2)

30-40: 입력값 검증을 추가하세요.

titlecontents 파라미터에 대한 검증이 없습니다. 빈 문자열, null, 길이 제한 등을 확인하는 검증 로직을 추가하는 것을 권장합니다.

예시:

@PostMapping("/create")
public String createQuestion(@RequestParam String title,
                             @RequestParam String contents,
                             HttpSession session) {
    User user = UserSessionUtils.getUserFromSession(session);
    if (user == null) {
        return "redirect:/user/loginForm";
    }
    
    if (title == null || title.trim().isEmpty()) {
        return "redirect:/qna/form"; // 또는 에러 메시지와 함께 폼으로 돌아가기
    }
    if (contents == null || contents.trim().isEmpty()) {
        return "redirect:/qna/form";
    }
    
    questionService.createQuestion(user.getUserId(), title, contents);
    return "redirect:/";
}

42-50: 더 명확한 에러 처리를 고려하세요.

질문을 찾을 수 없을 때 단순히 루트로 리다이렉트하는 것보다, 사용자에게 더 명확한 피드백을 제공하는 것이 좋습니다. 예를 들어, 에러 메시지를 표시하거나 404 페이지로 이동하는 것을 고려해보세요.

예시:

@GetMapping("/show")
public String showQuestion(@RequestParam Long questionId, Model model) {
    return questionService.findById(questionId)
            .map(question -> {
                model.addAttribute("question", question);
                return "qna/show";
            })
            .orElseGet(() -> {
                model.addAttribute("errorMessage", "질문을 찾을 수 없습니다.");
                return "error/404";
            });
}
src/main/java/jwp/controller/UserController.java (3)

28-32: 입력 검증 추가를 고려하세요.

사용자 입력에 대한 유효성 검사가 없습니다. @Valid 어노테이션과 BindingResult를 추가하여 잘못된 입력을 처리하는 것이 좋습니다.

다음과 같이 수정할 수 있습니다:

+import javax.validation.Valid;
+import org.springframework.validation.BindingResult;
+
 @PostMapping("/signup")
-public String createUser(@ModelAttribute User user) {
+public String createUser(@Valid @ModelAttribute User user, BindingResult bindingResult) {
+    if (bindingResult.hasErrors()) {
+        return "user/form";
+    }
     userService.createUser(user);
     return "redirect:/user/list";
 }

63-72: 접근 제어 로직 중복을 고려하세요.

Lines 65-68의 접근 제어 로직이 showUpdateForm 메서드(lines 51-54)와 거의 동일합니다. 이러한 중복은 향후 유지보수를 어렵게 만들 수 있습니다.

private 헬퍼 메서드로 추출하거나 인터셉터/AOP를 활용하는 것을 고려해보세요:

private boolean canAccessUser(HttpSession session, String userId) {
    User sessionUser = UserSessionUtils.getUserFromSession(session);
    return sessionUser != null && sessionUser.isSameUser(userId);
}

96-102: 세션 무효화 방식 개선을 고려하세요.

현재 구현은 두 가지 개선 가능한 부분이 있습니다:

  1. Spring이 주입하는 HttpSession 파라미터는 일반적으로 null이 아니므로 null 체크가 불필요합니다.
  2. 단일 속성만 제거하는 것보다 session.invalidate()를 사용하여 세션 전체를 무효화하는 것이 더 안전합니다.

다음과 같이 수정하는 것을 고려하세요:

 @GetMapping("/logout")
 public String logout(HttpSession session) {
-    if (session != null) {
-        session.removeAttribute(UserSessionUtils.USER_SESSION_KEY);
-    }
+    session.invalidate();
     return "redirect:/";
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42fac50 and 45795f8.

📒 Files selected for processing (12)
  • java-webMVC.iws (1 hunks)
  • src/main/java/jwp/controller/CreateQuestionController.java (1 hunks)
  • src/main/java/jwp/controller/CreateQuestionFormController.java (1 hunks)
  • src/main/java/jwp/controller/CreateUserController.java (1 hunks)
  • src/main/java/jwp/controller/ForwardController.java (1 hunks)
  • src/main/java/jwp/controller/LoginController.java (1 hunks)
  • src/main/java/jwp/controller/LogoutController.java (1 hunks)
  • src/main/java/jwp/controller/QuestionController.java (1 hunks)
  • src/main/java/jwp/controller/ShowQuestionController.java (1 hunks)
  • src/main/java/jwp/controller/UpdateUserController.java (1 hunks)
  • src/main/java/jwp/controller/UpdateUserFormController.java (1 hunks)
  • src/main/java/jwp/controller/UserController.java (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • src/main/java/jwp/controller/UpdateUserFormController.java
  • src/main/java/jwp/controller/LoginController.java
  • src/main/java/jwp/controller/CreateQuestionController.java
  • src/main/java/jwp/controller/LogoutController.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/jwp/controller/CreateQuestionFormController.java
  • src/main/java/jwp/controller/ForwardController.java
  • src/main/java/jwp/controller/CreateUserController.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/jwp/controller/UserController.java (2)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/QuestionController.java (1)
  • Controller (16-51)
src/main/java/jwp/controller/QuestionController.java (2)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/UserController.java (1)
  • Controller (17-103)
🔇 Additional comments (6)
src/main/java/jwp/controller/UpdateUserController.java (1)

1-30: UpdateUserController.java 파일 전체 삭제

이 파일은 완전히 주석 처리되어 있으며, 동일한 기능이 이미 UserController.java에 구현되어 있습니다. /user/update 엔드포인트와 updateUser 메서드가 UserController.java의 63-64줄에서 활성 상태로 존재합니다. PR의 "볼륨 축소" 목표에 맞추어 이 파일을 완전히 삭제하세요. Git 히스토리가 이전 코드를 보존하므로 주석 처리 상태로 유지할 필요가 없습니다.

src/main/java/jwp/controller/UserController.java (5)

23-26: LGTM!

회원가입 폼을 렌더링하는 로직이 명확하고 올바릅니다.


49-61: LGTM!

접근 제어 로직이 적절하고 Optional 패턴을 잘 활용하고 있습니다.


74-77: LGTM!

로그인 폼 렌더링 로직이 간결하고 올바릅니다.


79-89: LGTM!

인증 로직이 Optional 패턴을 적절히 활용하고 있으며, 세션 관리가 올바르게 구현되어 있습니다.


91-94: LGTM!

로그인 실패 페이지 렌더링 로직이 명확합니다.

Comment on lines 22 to 28
@GetMapping("/form")
public String showForm(HttpSession session) {
if (UserSessionUtils.isLogined(session)) {
return "qna/form.jsp";
}
return "redirect:/user/loginForm";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

뷰 경로 불일치를 수정하세요.

25번 줄에서는 "qna/form.jsp"를 반환하지만, 47번 줄에서는 "qna/show"를 반환합니다(.jsp 확장자 없음). Spring의 뷰 리졸버 설정에 따라 문제가 발생할 수 있으므로, 일관된 형식을 사용하는 것을 권장합니다.

 @GetMapping("/form")
 public String showForm(HttpSession session) {
     if (UserSessionUtils.isLogined(session)) {
-        return "qna/form.jsp";
+        return "qna/form";
     }
     return "redirect:/user/loginForm";
 }

Also applies to: 42-50

🤖 Prompt for AI Agents
In src/main/java/jwp/controller/QuestionController.java around lines 22-28 (and
also apply to lines 42-50), the controller returns a view name with the ".jsp"
extension ("qna/form.jsp") while other methods return logical view names without
extensions (e.g., "qna/show"); make the format consistent by removing the ".jsp"
extension and returning logical view names ("qna/form") everywhere (or
alternatively add ".jsp" to all returns if your project uses explicit
filenames), updating both the showForm method and the other methods in the 42-50
range to match.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
build.gradle (1)

29-29: MySQL 커넥터 의존성 업데이트 필요.

mysql:mysql-connector-java는 deprecated 되었으며, com.mysql:mysql-connector-j로 대체되어야 합니다. 명시적 버전도 지정하시기 바랍니다.

다음 diff를 적용하세요:

-    runtimeOnly 'mysql:mysql-connector-java'
+    runtimeOnly 'com.mysql:mysql-connector-j'
mysql-connector-j 최신 버전 및 Spring Boot 2.7.17과의 호환성 확인
java-webMVC.iws (1)

1-346: 워크스페이스 파일을 여전히 커밋하고 있습니다.

이전 리뷰에서도 지적되었듯이, .iws 파일은 개인 설정과 로컬 경로(Line 155, 170)를 포함하므로 버전 관리에서 제외해야 합니다.

.gitignore에 다음을 추가하고, 이미 추적 중인 파일을 제거하세요:

+*.iws
+*.iml
+.idea/workspace.xml
+.idea/tasks.xml

그 후 다음 명령으로 Git 인덱스에서 제거:

git rm --cached java-webMVC.iws
🧹 Nitpick comments (1)
build.gradle (1)

9-9: targetCompatibility 추가 고려.

sourceCompatibility만 설정하는 것보다 targetCompatibility도 함께 설정하는 것이 모범 사례입니다. Java 11을 대상으로 하는 경우:

 sourceCompatibility = '11'
+targetCompatibility = '11'
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45795f8 and 8673244.

📒 Files selected for processing (3)
  • build.gradle (1 hunks)
  • java-webMVC.iws (1 hunks)
  • src/main/java/core/jdbc/ConnectionManager.java (0 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/core/jdbc/ConnectionManager.java
🔇 Additional comments (1)
build.gradle (1)

25-25: R2DBC 풀 의존성의 필요성 확인 필요.

io.r2dbc:r2dbc-pool은 반응형(reactive) 데이터베이스 연결을 위한 의존성입니다. 현재 프로젝트는 전통적인 JPA/JDBC 방식을 사용 중인 것으로 보이므로, 이 의존성이 실제로 필요한지 확인하시기 바랍니다. 필요하지 않다면 제거하여 의존성을 간소화하세요.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

♻️ Duplicate comments (6)
java-webMVC.iws (1)

1-335: IDE 워크스페이스 파일을 버전 관리에서 제외하세요.

.iws 파일은 IntelliJ IDEA의 개인 워크스페이스 설정을 포함하며, 사용자별 로컬 경로(예: Line 144, 159의 C:/Users/samsung-user/...)와 개인 IDE 설정(작업 세션 기록, 최근 파일 등)을 담고 있습니다. 이러한 파일은 .gitignore에 추가하여 버전 관리에서 제외해야 합니다.

src/main/java/jwp/controller/CreateQuestionFormController.java (1)

16-18: 뷰 이름에서 .jsp 확장자를 제거하세요.

application.properties에서 spring.mvc.view.suffix=.jsp로 설정되어 있으므로, 뷰 이름에 .jsp를 포함하면 최종 경로가 /qna/form.jsp.jsp가 되어 404 오류가 발생합니다.

다음과 같이 수정하세요:

-            return "qna/form.jsp";
+            return "qna/form";
src/main/java/jwp/controller/LoginController.java (1)

19-29: 세션 고정 취약점 해결 필요

로그인 성공 후에도 기존 HttpSession ID를 그대로 유지하면 세션 고정(session fixation) 공격에 노출됩니다. 인증 직후 request.changeSessionId()를 호출해 세션 ID를 재발급하도록 수정해 주세요.

다음과 같이 수정하세요:

+import javax.servlet.http.HttpServletRequest;
+
 @Controller
 @RequestMapping("/user")
 @RequiredArgsConstructor
 public class LoginController {
     private final UserService userService;

     @PostMapping("/login")
     public String login(@RequestParam String userId,
                         @RequestParam String password,
+                        HttpServletRequest request,
                         HttpSession session) {
         return userService.authenticate(userId, password)
                 .map(user -> {
+                    request.changeSessionId();
                     session.setAttribute(UserSessionUtils.USER_SESSION_KEY, user);
                     return "redirect:/";
                 })
                 .orElse("redirect:/user/loginFailed");
     }
 }
src/main/java/jwp/controller/UserController.java (3)

36-38: 중복된 HTTP 메서드 체크를 제거하세요.

@GetMapping 어노테이션이 이미 GET 메서드만 허용하므로, 수동 메서드 체크는 불필요합니다.

다음과 같이 수정하세요:

 @GetMapping("/list")
 public String listUsers(HttpServletRequest req, Model model) {
-    if (!"GET".equalsIgnoreCase(req.getMethod())) {
-        return "redirect:/user/login";
-    }
-
     HttpSession session = req.getSession(false);

40-43: 세션 검증을 UserSessionUtils로 통일하세요.

다른 컨트롤러들은 UserSessionUtils.isLogined(session)을 사용하여 로그인 여부를 확인하지만, 여기서는 직접 session == null만 체크하고 있어 일관성이 없습니다. 세션이 존재하더라도 사용자 정보가 없을 수 있습니다.

다음과 같이 수정하세요:

-    HttpSession session = req.getSession(false);
-    if (session == null) {
-        return "redirect:/user/login";
+    if (!UserSessionUtils.isLogined(req.getSession(false))) {
+        return "redirect:/user/loginForm";
     }

46-46: 뷰 이름에서 .jsp 확장자를 제거하세요.

application.properties에서 spring.mvc.view.suffix=.jsp로 설정되어 있으므로, 뷰 리졸버가 자동으로 .jsp를 추가합니다. 현재 코드는 "user/list.jsp"를 반환하므로 최종 경로가 "/user/list.jsp.jsp"가 되어 잘못된 경로가 됩니다.

다음과 같이 수정하세요:

-    return "user/list.jsp";
+    return "user/list";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8673244 and 406a4dd.

📒 Files selected for processing (16)
  • java-webMVC.iws (1 hunks)
  • src/main/java/WebServerLauncher.java (1 hunks)
  • src/main/java/jwp/controller/Controller.java (1 hunks)
  • src/main/java/jwp/controller/CreateQuestionController.java (1 hunks)
  • src/main/java/jwp/controller/CreateQuestionFormController.java (1 hunks)
  • src/main/java/jwp/controller/CreateUserController.java (1 hunks)
  • src/main/java/jwp/controller/DispatcherServlet.java (1 hunks)
  • src/main/java/jwp/controller/ForwardController.java (1 hunks)
  • src/main/java/jwp/controller/ListUserController.java (1 hunks)
  • src/main/java/jwp/controller/LoginController.java (1 hunks)
  • src/main/java/jwp/controller/LogoutController.java (1 hunks)
  • src/main/java/jwp/controller/RequestMapping.java (1 hunks)
  • src/main/java/jwp/controller/ShowQuestionController.java (1 hunks)
  • src/main/java/jwp/controller/UpdateUserController.java (1 hunks)
  • src/main/java/jwp/controller/UpdateUserFormController.java (1 hunks)
  • src/main/java/jwp/controller/UserController.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/WebServerLauncher.java
🧰 Additional context used
🧬 Code graph analysis (12)
src/main/java/jwp/controller/UpdateUserController.java (4)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/UpdateUserFormController.java (1)
  • Controller (15-34)
src/main/java/jwp/controller/UserController.java (1)
  • Controller (17-103)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
src/main/java/jwp/controller/CreateQuestionFormController.java (3)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/QuestionController.java (1)
  • Controller (16-51)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
src/main/java/jwp/controller/ForwardController.java (5)
src/main/java/jwp/controller/CreateQuestionFormController.java (1)
  • Controller (10-21)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/LogoutController.java (1)
  • Controller (10-21)
src/main/java/jwp/controller/UserController.java (1)
  • Controller (17-103)
src/main/java/jwp/controller/QuestionController.java (1)
  • Controller (16-51)
src/main/java/jwp/controller/CreateQuestionController.java (4)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/CreateQuestionFormController.java (1)
  • Controller (10-21)
src/main/java/jwp/controller/QuestionController.java (1)
  • Controller (16-51)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
src/main/java/jwp/controller/CreateUserController.java (2)
src/main/java/jwp/controller/UserController.java (1)
  • Controller (17-103)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
src/main/java/jwp/controller/LoginController.java (5)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/ForwardController.java (1)
  • Controller (6-23)
src/main/java/jwp/controller/LogoutController.java (1)
  • Controller (10-21)
src/main/java/jwp/controller/UserController.java (1)
  • Controller (17-103)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
src/main/java/jwp/controller/ListUserController.java (2)
src/main/java/core/db/MemoryUserRepository.java (1)
  • MemoryUserRepository (9-41)
src/main/java/jwp/controller/DispatcherServlet.java (1)
  • WebServlet (14-62)
src/main/java/jwp/controller/UserController.java (7)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/CreateUserController.java (1)
  • Controller (11-22)
src/main/java/jwp/controller/ForwardController.java (1)
  • Controller (6-23)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/LogoutController.java (1)
  • Controller (10-21)
src/main/java/jwp/controller/UpdateUserController.java (1)
  • Controller (15-30)
src/main/java/jwp/controller/UpdateUserFormController.java (1)
  • Controller (15-34)
src/main/java/jwp/controller/DispatcherServlet.java (1)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
src/main/java/jwp/controller/UpdateUserFormController.java (5)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/UpdateUserController.java (1)
  • Controller (15-30)
src/main/java/jwp/controller/UserController.java (1)
  • Controller (17-103)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
src/main/java/jwp/controller/ShowQuestionController.java (2)
src/main/java/jwp/controller/QuestionController.java (1)
  • Controller (16-51)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
src/main/java/jwp/controller/LogoutController.java (3)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/LoginController.java (1)
  • Controller (13-30)
src/main/java/jwp/controller/RequestMapping.java (1)
  • RequestMapping (7-28)
🔇 Additional comments (3)
src/main/java/jwp/controller/LogoutController.java (1)

14-20: 로그아웃 로직이 올바르게 구현되었습니다.

세션 null 체크를 수행한 후 사용자 세션 속성을 제거하는 로직이 정확합니다.

src/main/java/jwp/controller/UpdateUserController.java (1)

20-29: 사용자 업데이트 로직이 올바르게 구현되었습니다.

세션 사용자 검증, 권한 확인, 업데이트 후 세션 갱신 로직이 모두 적절하게 구현되어 있습니다.

src/main/java/jwp/controller/Controller.java (1)

9-10: 마이그레이션 상태 확인 결과: 아직 레거시 패턴이 활발히 사용 중

코드 검증 결과, 이 인터페이스는 여전히 적극적으로 사용되고 있습니다. ListUserControllerController 인터페이스를 구현하고 있으며, RequestMapping 클래스와 DispatcherServlet이 이를 참조하고 있습니다. 따라서 현재 상태에서는 인터페이스 제거가 불가능하고, Spring MVC로의 마이그레이션이 부분적으로만 진행된 상태입니다.

  • ListUserController (라인 18): 여전히 Controller 인터페이스 구현
  • RequestMapping: Map<String, Controller> 사용 및 다수 레거시 컨트롤러 인스턴스화
  • DispatcherServlet (라인 54): 여전히 Controller 인터페이스 타입 사용

리뷰 의견은 "완전히 마이그레이션했다면"이라는 전제 조건을 기반으로 하는데, 현재 코드베이스는 이 조건을 만족하지 않습니다. 마이그레이션을 완료한 후 인터페이스 제거를 검토하시기 바랍니다.

Likely an incorrect or invalid review comment.


@Controller
public class ForwardController {
// private final String viewName;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

주석 처리된 코드를 제거하세요.

사용하지 않는 코드는 버전 관리 시스템에 기록되므로, 주석으로 남기지 말고 삭제하는 것이 좋습니다.

-//    private final String viewName;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// private final String viewName;
🤖 Prompt for AI Agents
In src/main/java/jwp/controller/ForwardController.java around line 8, there's a
commented-out unused field declaration ("private final String viewName;");
remove this commented line entirely (do not leave it commented) so the file
contains only active, necessary code and let version control track history if
needed.

Comment on lines 9 to 22
@GetMapping("/user/loginForm")
public String loginForm() {
return "user/login";
}

@GetMapping("/user/loginFailed")
public String loginFailed() {
return "user/loginFailed";
}

@GetMapping("/user/form")
public String signUpForm() {
return "user/form";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

UserController와의 기능 중복 확인 필요

UserController에도 동일한 기능의 엔드포인트들이 주석 처리되어 있습니다(lines 56-61, 73-76, 19-22). 리팩토링 중간 단계로 보이지만, 최종적으로 하나의 컨트롤러로 통합하거나 역할을 명확히 분리해야 합니다.

다음 스크립트로 현재 활성화된 엔드포인트 매핑을 확인하세요:


🏁 Script executed:

#!/bin/bash
# Description: Find all active endpoint mappings for /user paths

echo "=== Active @GetMapping for /user paths ==="
rg -n --type=java -B5 '@GetMapping.*"/user/' | rg -v '^--$'

echo "=== Checking for conflicts ==="
rg -n --type=java '@GetMapping\("/user/(loginForm|loginFailed|form)"\)'

Length of output: 1602


추가 검증이 필요합니다. UserController의 지정된 라인을 확인하겠습니다.


---

🏁 Script executed:

```shell
#!/bin/bash
# Description: Check UserController for commented-out endpoints

echo "=== UserController full content ==="
find . -name "UserController.java" -type f | head -1 | xargs cat -n

echo -e "\n=== Specific lines in UserController ==="
find . -name "UserController.java" -type f | head -1 | xargs sed -n '1,100p' | cat -n

Length of output: 8588


/user/form 엔드포인트 중복 제거 필요

ForwardController와 UserController에 동일한 엔드포인트가 중복되어 있습니다:

  • UserController (line 23): @GetMapping("/form") → "user/form"
  • ForwardController (line 19): @GetMapping("/user/form") → "user/form"

두 엔드포인트 모두 활성화되어 있으며 동일한 응답을 반환합니다. 하나의 컨트롤러로 통합하여 역할을 명확히 분리해야 합니다. UserController의 주석 처리된 /loginForm, /loginFailed 엔드포인트(lines 74-77, 91-94)도 함께 정리하시기 바랍니다.

🤖 Prompt for AI Agents
In src/main/java/jwp/controller/ForwardController.java around lines 9 to 22, the
@GetMapping("/user/form") duplicates an endpoint already declared in
UserController (line ~23) and both return "user/form"; remove the duplicate
mapping from ForwardController and consolidate the responsibility for the
/user/form route into UserController (or vice versa) so only one controller
serves that path; also clean up the commented /loginForm and /loginFailed blocks
in UserController (lines ~74-77 and ~91-94) by either restoring them into
UserController if they belong there or deleting them to avoid confusion; update
any internal references/tests to point to the single chosen controller.

Comment on lines 10 to 22
public void init() {
mappings.put("/", new HomeController());
mappings.put("/user/login", new LoginController());
mappings.put("/user/loginForm", new ForwardController("/user/login.jsp"));
mappings.put("/user/logout", new LogoutController());
mappings.put("/user/updateForm", new UpdateUserFormController());
mappings.put("/user/update", new UpdateUserController());
mappings.put("/user/list", new ListUserController());
mappings.put("/user/loginFailed", new ForwardController("/user/loginFailed.jsp"));
mappings.put("/qna/form", new CreateQuestionFormController());
mappings.put("/qna/create", new CreateQuestionController());
mappings.put("/qna/show", new ShowQuestionController());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Spring MVC와 호환되지 않는 수동 컨트롤러 인스턴스화

이 클래스는 여러 심각한 문제를 가지고 있습니다:

  1. 컴파일 오류: Line 13과 18에서 new ForwardController("/user/login.jsp")를 호출하지만, ForwardController는 더 이상 String 파라미터를 받는 생성자를 가지고 있지 않습니다.

  2. Spring DI 우회: 모든 컨트롤러를 수동으로 인스턴스화하고 있어 Spring의 의존성 주입이 작동하지 않습니다. 예를 들어, LoginControllerUserService를 주입받아야 하는데, 이 방식으로는 null이 됩니다.

  3. 아키텍처 불일치: Spring MVC는 자체 DispatcherServlet과 어노테이션 기반 매핑을 제공하므로, 이러한 수동 매핑 클래스는 불필요합니다.

Spring Boot로 마이그레이션했다면 이 클래스를 제거하고, Spring MVC의 표준 라우팅 메커니즘을 사용하세요. 만약 레거시 코드와의 호환성을 위해 필요하다면, 다음과 같이 리팩토링이 필요합니다:

@Component
public class RequestMapping {
    private final Map<String, Controller> mappings = new HashMap<>();
    
    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void init() {
        // Spring 컨텍스트에서 빈을 가져와야 함
        mappings.put("/", applicationContext.getBean(HomeController.class));
        // ... 나머지도 동일
    }
}

하지만 Spring MVC를 사용한다면 이 클래스 자체가 불필요합니다.

🤖 Prompt for AI Agents
In src/main/java/jwp/controller/RequestMapping.java around lines 10 to 22, the
class currently manually instantiates controller implementations (causing Spring
DI to be bypassed) and calls a ForwardController constructor that no longer
accepts a String (causing compile errors); either remove this class entirely
when migrating to Spring MVC and rely on @Controller/@RequestMapping handlers
and DispatcherServlet, or refactor it into a Spring-managed component by
annotating it with @Component, injecting ApplicationContext (or specific
controller beans) and using @PostConstruct to populate mappings with
applicationContext.getBean(YourController.class) so controllers receive
dependencies, and update the ForwardController usages to call the
constructor/signature that currently exists (or obtain a bean of the correct
ForwardController type) to resolve the compilation errors.

Comment on lines 28 to 32
@PostMapping("/signup")
public String createUser(@ModelAttribute User user) {
userService.createUser(user);
return "redirect:/user/list";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

CreateUserController와 중복된 메서드를 제거하세요.

CreateUserController에 동일한 엔드포인트(POST /user/signup)를 처리하는 createUser() 메서드가 이미 존재합니다. 두 컨트롤러가 같은 경로를 처리하면 라우팅 충돌이 발생합니다.

이 메서드를 제거하거나 CreateUserController의 해당 메서드를 제거하여 중복을 해소하세요:

-    @PostMapping("/signup")
-    public String createUser(@ModelAttribute User user) {
-        userService.createUser(user);
-        return "redirect:/user/list";
-    }
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@PostMapping("/signup")
public String createUser(@ModelAttribute User user) {
userService.createUser(user);
return "redirect:/user/list";
}
🤖 Prompt for AI Agents
In src/main/java/jwp/controller/UserController.java around lines 28 to 32, there
is a duplicate POST /signup handler createUser(@ModelAttribute User user) that
conflicts with the identical method in CreateUserController; remove this method
from UserController (or alternatively remove the one in CreateUserController) to
eliminate the routing conflict, then clean up any now-unused imports and
references and run the application/tests to ensure the signup endpoint still
works from the remaining controller.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/main/java/jwp/controller/UserController.java (1)

88-97: 세션 고정 취약점 차단을 위한 세션 ID 재발급 필요
로그인 성공 후 기존 세션 ID를 그대로 쓰면 세션 고정 공격에 노출됩니다. HttpServletRequest를 받아 request.changeSessionId() 호출 후 세션에 사용자 정보를 넣어 주세요. 이전 리뷰에서도 동일한 문제가 지적되었습니다.

-    public String login(@RequestParam String userId,
-                        @RequestParam String password,
-                        HttpSession session) {
+    public String login(@RequestParam String userId,
+                        @RequestParam String password,
+                        HttpServletRequest request,
+                        HttpSession session) {
         return userService.authenticate(userId, password)
                 .map(user -> {
+                    request.changeSessionId();
                     session.setAttribute(UserSessionUtils.USER_SESSION_KEY, user);
                     return "redirect:/";
                 })
java-webMVC.iws (1)

1-327: 이전에 지적된 문제가 여전히 존재합니다.

.iws 파일은 여전히 버전 관리에 포함되어 있으며, 개인 로컬 경로(Line 147, 162)를 포함하고 있습니다. 이전 리뷰에서 제안된 대로 .gitignore에 추가하여 버전 관리에서 제외해야 합니다.

🧹 Nitpick comments (4)
src/main/java/jwp/controller/CreateUserController.java (1)

1-22: 중복 엔드포인트 이슈는 해결되었으나, 주석 처리된 파일은 삭제하는 것이 좋습니다.

이전 리뷰에서 지적된 중복 매핑 문제가 전체 파일을 주석 처리하여 해결되었습니다. 그러나 주석 처리된 코드를 남겨두는 것은 코드베이스를 혼란스럽게 만들고 유지보수를 어렵게 합니다.

이 파일을 완전히 삭제하는 것을 권장합니다:

#!/bin/bash
# 파일 삭제 명령어 (참고용)
git rm src/main/java/jwp/controller/CreateUserController.java

만약 향후 참고를 위해 코드를 보존하고 싶다면, Git 히스토리에 이미 기록되어 있으므로 필요시 언제든 복구할 수 있습니다.

src/main/java/jwp/controller/HomeController.java (1)

20-25: 주석 처리된 중복 코드 정리 제안
이미 위에서 동일 구현이 활성화돼 있으므로 이 주석 블록은 유지보수 잡음을 유발합니다. 깔끔하게 삭제해 주세요.

src/main/java/jwp/controller/LoginController.java (1)

1-30: 주석만 남은 컨트롤러 정리 필요
파일 전체가 주석이라 실제 동작이 전혀 없습니다. 사용하지 않는 클래스라면 삭제하고, 필요하다면 활성 코드로 구현을 복구해 주세요.

src/main/java/jwp/controller/RequestMapping.java (1)

1-28: 불필요한 레거시 매핑 클래스 제거 권장
Spring MVC 전환 후에는 수동 매핑이 불필요합니다. 전부 주석 처리된 상태이기도 하니, 파일 자체를 삭제해 혼란을 줄이는 편이 좋겠습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 406a4dd and d4e1a8e.

📒 Files selected for processing (19)
  • java-webMVC.iws (1 hunks)
  • src/main/java/WebServerLauncher.java (0 hunks)
  • src/main/java/jwp/WebServerLauncher.java (1 hunks)
  • src/main/java/jwp/controller/Controller.java (1 hunks)
  • src/main/java/jwp/controller/CreateQuestionController.java (1 hunks)
  • src/main/java/jwp/controller/CreateQuestionFormController.java (1 hunks)
  • src/main/java/jwp/controller/CreateUserController.java (1 hunks)
  • src/main/java/jwp/controller/DispatcherServlet.java (1 hunks)
  • src/main/java/jwp/controller/ForwardController.java (1 hunks)
  • src/main/java/jwp/controller/HomeController.java (1 hunks)
  • src/main/java/jwp/controller/ListUserController.java (1 hunks)
  • src/main/java/jwp/controller/LoginController.java (1 hunks)
  • src/main/java/jwp/controller/LogoutController.java (1 hunks)
  • src/main/java/jwp/controller/QuestionController.java (1 hunks)
  • src/main/java/jwp/controller/RequestMapping.java (1 hunks)
  • src/main/java/jwp/controller/ShowQuestionController.java (1 hunks)
  • src/main/java/jwp/controller/UpdateUserController.java (1 hunks)
  • src/main/java/jwp/controller/UpdateUserFormController.java (1 hunks)
  • src/main/java/jwp/controller/UserController.java (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/WebServerLauncher.java
✅ Files skipped from review due to trivial changes (3)
  • src/main/java/jwp/controller/LogoutController.java
  • src/main/java/jwp/controller/Controller.java
  • src/main/java/jwp/controller/CreateQuestionFormController.java
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/main/java/jwp/controller/DispatcherServlet.java
  • src/main/java/jwp/controller/QuestionController.java
  • src/main/java/jwp/controller/UpdateUserFormController.java
  • src/main/java/jwp/controller/UpdateUserController.java
  • src/main/java/jwp/controller/ListUserController.java
  • src/main/java/jwp/controller/ShowQuestionController.java
  • src/main/java/jwp/controller/ForwardController.java
🧰 Additional context used
🧬 Code graph analysis (2)
src/main/java/jwp/controller/UserController.java (2)
src/main/java/jwp/support/session/UserSessionUtils.java (1)
  • UserSessionUtils (7-27)
src/main/java/jwp/controller/QuestionController.java (1)
  • Controller (16-51)
src/main/java/jwp/controller/HomeController.java (1)
src/main/java/jwp/controller/QuestionController.java (1)
  • Controller (16-51)

Comment on lines +1 to +37
//package jwp.controller;
//
//
//import jwp.model.Question;
//import jwp.model.User;
//import jwp.service.QuestionService;
//import jwp.support.session.UserSessionUtils;
//import lombok.RequiredArgsConstructor;
//import org.springframework.stereotype.Controller;
//import org.springframework.web.bind.annotation.PostMapping;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RequestParam;
//
//import javax.servlet.ServletException;
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import javax.servlet.http.HttpSession;
//import java.io.IOException;
//import java.sql.SQLException;
//
//@Controller
//@RequestMapping("/qna")
//@RequiredArgsConstructor
//public class CreateQuestionController{
// private final QuestionService questionService;
// @PostMapping("/create")
// public String createQuestion(@RequestParam String title,
// @RequestParam String contents,
// HttpSession session) {
// User user = UserSessionUtils.getUserFromSession(session);
// if (user == null) {
// return "redirect:/user/loginForm";
// }
// questionService.createQuestion(user.getUserId(), title, contents);
// return "redirect:/";
// }
//}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

주석 처리된 파일을 삭제하세요.

전체 파일이 주석 처리되어 있어 실행되지 않는 데드 코드입니다. 이전 리뷰에서 지적된 중복 매핑 문제를 주석 처리로 회피했지만, 이는 근본적인 해결책이 아닙니다. 버전 관리 시스템에 코드 이력이 남아있으므로 필요시 복원할 수 있습니다.

코드베이스를 깔끔하게 유지하기 위해 이 파일을 완전히 삭제해주세요.

🤖 Prompt for AI Agents
In src/main/java/jwp/controller/CreateQuestionController.java around lines 1-37
the entire file is commented out and represents dead code left to avoid a
duplicate-mapping issue; delete this file from the repository. Remove the file
(git rm) and commit the change, then run a project build to ensure no remaining
references/imports break compilation; if any other controllers referenced this
class, resolve those references (prefer fixing duplicates rather than
reintroducing this file).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant