Planet Now 프로젝트 개발 중 발생한 문제와 해결 방법
개발을 진행하면서 다양한 문제를 마주했고, 이를 해결하기 위해 여러 방법을 시도했다. 특히 Thymeleaf + MyBatis 조합을 사용하면서 발생한 이슈와 비동기 처리, 데이터베이스 연동에서 발생한 문제들이 많았다. 이번 포스팅에서는 내가 겪은 주요 문제들과 해결 방법을 정리했다.
- 문제 발생:
-
전체 게시글을 조회할 때 한 번에 모든 데이터를 불러오면서 서버에 부하가 발생할 가능성이 있었다. 데이터를 한꺼번에 가져오면 응답 시간이 길어지고, 서버의 메모리 사용량이 증가할 수 있다.
JPA의 Pagination 기능을 사용하면 쉽게 해결할 수 있지만, 현재 프로젝트는 MyBatis를 사용하고 있어 코드의 일관성을 유지하기 위해 MyBatis에서 직접 페이징 기능을 구현해야 했다.
- 해결 방법:
-
MyBatis에서 페이징을 적용하기 위해 LIMIT과 OFFSET을 활용하여 필요한 개수만큼 데이터를 가져오도록 쿼리를 수정했다.
컨트롤러에서 page와 size 값을 받아 offset을 계산한 후, 이를 MyBatis의 매개변수로 전달하여 해당 페이지의 데이터만 조회할 수 있도록 했다.
MyBatis로 게시글 페이징 처리
컨트롤러에서 받은 page와 size값 service에 적용 @Override
public List
쿼리에 LIMIT과 OFFSET 추가LIMIT #{size} OFFSET #{offset}
이를 통해 한 번에 불러오는 데이터량을 제한하면서도, 사용자가 원하는 페이지의 데이터를 효율적으로 조회할 수 있도록 개선했다..
- 문제 발생:
-
전체 게시글 조회 페이지에서 특정 키워드와 카테고리를 이용한 검색 기능을 개발했다.
String keyword와 String category를 MyBatis에 전달하여 일치하는 데이터를 조회해야 했지만, MyBatis에서 keyword와 category 값이 계속 null로 인식되는 문제가 발생했다.
컨트롤러에서 정상적으로 값이 전달되고 있음에도 불구하고 MyBatis에 전달될 때 null이 되어 쿼리가 정상적으로 실행되지 않았다.
- 해결 방법:
-
keyword와 category를 Map으로 묶어 MyBatis로 전달하도록 변경했다.
MyBatis에서 parameterType="map"을 사용하여 데이터를 올바르게 받도록 수정했다.
MyBatis를 이용해 게시글을 keyword와 category로 불러올 때 값이 null로 인식되는 문제
@Override
public List
이제 keyword와 category 값이 정상적으로 전달되어 검색이 정상적으로 동작함.
- 문제 발생:
-
비동기 AJAX 요청을 통해 태스크 진행 상태를 업데이트했지만, 변경된 상태가 화면에 즉시 반영되지 않음..
상태가 변경되었음에도 불구하고, 사용자가 페이지를 수동으로 새로고침하지 않으면 화면에서 변화를 확인할 수 없었다.
- 해결 방법:
-
AJAX 요청 성공 시 location.reload()를 사용하여 페이지를 새로고침하여 변경된 상태를 반영하도록 수정.
AJAX요청으로 태스크 진행 상태를 변경한 후 화면에 즉시 반영되지 않는 문제
$.ajax({
url: "/sub-task/change-status",
type: "POST",
contentType: "application/json",
data: JSON.stringify({ subTaskId: subTaskId,
taskStatusId: taskStatusId }),
success: function (response) {
// alert("상태가 업데이트되었습니다");
location.reload()
},
이제 상태 변경 후 자동으로 업데이트되어 사용자가 새로고침하지 않아도 변경 사항이 반영됨.
- 문제 발생:
-
백엔드에서는 LocalDate.now()를 사용하여 날짜를 처리했지만, 데이터베이스에서는 timestamp 타입을 사용하여 초 단위까지 저장하고 있었다.
LocalDate.now()는 기본적으로 초 단위를 포함하지 않기 때문에 데이터를 비교할 때 문제가 발생했다.
- 해결 방법:
-
DB 쿼리에서 날짜를 변환하여 맞춤.
DATE_FORMAT(변수명, '%Y-%m-%d') 형식으로 변환하여 LocalDate와 비교할 수 있도록 수정.
프론트엔드에서도 날짜 포맷을 명시적으로 지정하여 바인딩.
Java의 LocalDate와 DB의 timestamp 불일치로 인한 오류 발생
SQL 쿼리 수정SELECT DATE_FORMAT(M.ENROLLED_AT, '%Y-%m-%d') AS enrolledDate FROM members;
Thymeleaf에서 날짜 포맷 적용<"input type="date" th:value="${#temporals.format(task.dueDate, 'yyyy-MM-dd')}"/
이제 Java와 DB 간 날짜 불일치 문제가 해결되어 정상적으로 동작함.
- 문제 발생:
-
여러 개의 Task를 한 번에 추가해야 하는 기능을 개발 중이었음.
기존에는 Java에서 for 문을 사용하여 개별적으로 INSERT 쿼리를 실행했지만, 이 방식은 성능이 매우 떨어지는 문제가 있었다. 반복되는 쿼리 실행을 해서 그렇다.
- 해결 방법:
-
DB에서 MyBatis의 foreach를 사용하여 한 번의 INSERT로 여러 개의 데이터를 처리하도록 변경.
여러 개의 Task를 INSERT할 때 성능 저하 발생
기존코드 예시for (Task task : taskList) {
taskMapper.insertTask(task);
}
개선된 코드 (MyBatis의 foreach 사용) 예시insert id="insertTasks"
INSERT INTO tasks (title, status) VALUES
(#{task.title}, #{task.status})
/insert
이제 한 번의 쿼리 실행으로 여러 개의 데이터를 처리할 수 있어 성능이 개선됨.
- 문제 발생:
-
로그인 후 사용자가 원래 보려던 페이지로 이동해야 하지만, 로그인 성공 후 무조건 홈 화면으로 이동하는 문제 발생.
- 해결 방법:
-
로그인 페이지에서 document.referrer 값을 확인하여 사용자가 원래 있던 페이지가 현재 웹사이트의 도메인과 일치하면 해당 페이지로 이동하도록 수정.
로그인 후 원래 보려던 페이지로 이동하지 않는 문제
JavaScript 코드 수정const referrer = document.referrer;
if (referrer && referrer.includes(window.location.host)) {
window.location.href = referrer; // 같은 사이트 내에서만 이동
} else {
window.location.href = "/home"; // 외부 사이트일 경우 홈 화면으로 이동
}
이제 로그인 후 사용자가 원래 보려던 페이지로 자연스럽게 이동됨.
- 문제 발생:
-
stomp.js를 이용하여 클라이언트에서 JSON 형태로 메시지를 서버에 보냈다.
하지만 서버에서 메시지를 받을 때 JSON이 HTML로 변환되지 않고 그대로 출력되는 문제 발생.
이는 XSS(크로스 사이트 스크립팅) 공격 위험이 있는 보안 문제이기도 했다.
- 해결 방법:
-
@Payload를 사용하여 메시지를 받을 때 자동으로 변환되도록 설정하여 해결.
WebSocket으로 채팅 구현 시 JSON 메시지가 그대로 출력되는 문제
@MessageMapping("/chat")
@SendTo("/topic/messages")
public ChatMessage receiveMessage(@Payload ChatMessage message) {
return message;
}
이제 메시지가 정상적으로 변환되어 출력되며, 보안 문제도 해결됨.
Posted on 2025.02.19