Troubleshooting: 무엇이 문제였는가?/본캠프 3주 차: 일정 관리 앱 만들기

1단계: "넌 띄어쓰기를 소중히 하지 않았어"

writingforever162 2024. 12. 9. 14:39

[문제]

SQL 쿼리(query)문을 작성하고 실행하니 바로 500 Internal Server Error 메시지가 떴다. 이 정도면 저 오류 메시지를 보려고 리팩토링(refactoring)하는 게 아닌가 싶었다.

 

[원인]

package com.spring.weekthree.repository;

import com.spring.weekthree.dto.responsedto.PlanResponseDto;
import com.spring.weekthree.entity.Plan;
import org.springframework.http.HttpStatus;
import org.springframework.jdbc.core.*;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import org.springframework.web.server.ResponseStatusException;

import javax.sql.DataSource;
import java.sql.*;
import java.sql.Date;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

// Data Access Layer(Repository Layer)
@Repository
public class JdbcTemplatePlanRepository implements PlanRepository {
    private final JdbcTemplate jdbcTemplate;

    public JdbcTemplatePlanRepository(DataSource dataSource) {

        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public PlanResponseDto save(Plan plan) {
        SimpleJdbcInsert jdbcInsert;

        jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);

        jdbcInsert.withTableName("planner").
                usingGeneratedKeyColumns("id");

        Map<String, Object> parameters = new HashMap<>();

        parameters.put("name", plan.getName());
        parameters.put("password", plan.getPassword());
        parameters.put("plannedDate", plan.getPlannedDate());
        parameters.put("title", plan.getTitle());
        parameters.put("task", plan.getTask());
        parameters.put("createdDateTime", plan.getCreatedDateTime());
        parameters.put("updatedDateTime", plan.getUpdatedDateTime());

        Number key = jdbcInsert.executeAndReturnKey(
                new MapSqlParameterSource(parameters
                )
        );

        return new PlanResponseDto(
                key.longValue(),
                plan.getName(),
                plan.getPlannedDate(),
                plan.getTitle(),
                plan.getTask(),
                plan.getCreatedDateTime(),
                plan.getUpdatedDateTime());
    }

    @Override
    public List<PlanResponseDto> fetchAllPlans(
            String name,
            LocalDate updatedDate
    ) {
        StringBuilder sql;

        sql = new StringBuilder("SELECT * FROM planner WHERE 1=1");

        List<Object> params = new ArrayList<>();

        if (name != null) {
            sql.append(" AND BINARY name = ? ");
            params.add(name);
        }
        /*
        [수정 전] sql.append("AND BINARY name = ? ");
        [수정 후] sql.append(" AND BINARY name = ? ");
         */

        if (updatedDate != null) {
            Date updatedDateSql = Date.valueOf(updatedDate);
            sql.append(" AND DATE(updatedDateTime) = ? ");
            params.add(updatedDateSql);
        }
        /*
        [수정 전] sql.append("AND DATE(updatedDateTime) = ? ");
        [수정 후] sql.append(" AND DATE(updatedDateTime) = ? ");
         */

        sql.append(" ORDER BY updatedDateTime DESC");
        /*
        [수정 전] sql.append("ORDER BY updatedDateTime DESC");
        [수정 후] sql.append(" ORDER BY updatedDateTime DESC");
         */

        List<PlanResponseDto> allPlans;

        allPlans = jdbcTemplate.query(
                sql.toString(),
                plannerRowMapper(),
                params.toArray()
        );
        return allPlans;
    }

    @Override
    public Plan fetchPlanById0rElseThrow(Long id) {

        List<Plan> result = jdbcTemplate.query(
                "SELECT * FROM planner WHERE id =?",
                plannerRowMapperEach(),
                id
        );
        return result.stream()
                .findAny()
                .orElseThrow(
                        () -> new ResponseStatusException(
                                HttpStatus.NOT_FOUND,
                                "Id does no exist id = " + id
                        )
                );
    }

    @Override
    public int updatePatchInRepository(
            Long id,
            String name,
            LocalDate plannedDate,
            String title,
            String task,
            LocalDateTime updatedDateTime
    ) {
        return jdbcTemplate.update(
                "UPDATE planner SET " +
                        "name = ?, " +
                        "plannedDate = ?, " +
                        "title = ?, " +
                        "task = ?, " +
                        "updatedDateTime = ? " +
                        "WHERE id = ?",
                name,
                plannedDate,
                title,
                task,
                updatedDateTime,
                id
        );
    }

    @Override
    public void deletePlan(Long id) {
        jdbcTemplate.update(
                "DELETE FROM planner WHERE id = ?",
                id
        );
    }

    private RowMapper<PlanResponseDto> plannerRowMapper() {
        return new RowMapper<PlanResponseDto>() {
            @Override
            public PlanResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
                return new PlanResponseDto(
                        rs.getLong("id"),
                        rs.getString("name"),
                        rs.getDate("plannedDate").toLocalDate(),
                        rs.getString("title"),
                        rs.getString("task"),
                        rs.getTimestamp("createdDateTime").toLocalDateTime(),
                        rs.getTimestamp("updatedDateTime").toLocalDateTime()
                );
            }
        };
    }

    private RowMapper<Plan> plannerRowMapperEach() {
        return new RowMapper<Plan>() {
            @Override
            public Plan mapRow(ResultSet rs, int rowNum) throws SQLException {
                return new Plan(
                        rs.getLong("id"),
                        rs.getString("name"),
                        rs.getString("password"),
                        rs.getDate("plannedDate").toLocalDate(),
                        rs.getString("title"),
                        rs.getString("task"),
                        rs.getTimestamp("createdDateTime").toLocalDateTime(),
                        rs.getTimestamp("updatedDateTime").toLocalDateTime()
                );
            }
        };
    }
}

원인은 띄어쓰기를 제대로 쓰지 않은 데에 있었다. 띄어쓰기가 없으니, 문장이 추가될 때마다 두 단어가 하나로 합쳐지며 이상한 문장이 완성되었다. SQL을 처음 공부하는 티가 쿼리(query)문을 작성할 때 바로 드러날 줄이야.

 

[해결]

문제를 해결한 뒤에는 처음에 구현한 대로 조회 기능이 제대로 작동했다. 혹시나 하는 마음에 작성자 이름을 의미하는 name과 수정 날짜 및 시간인 updatedDateTime을 수정 날짜로 바꾸어서 조회했는데, 다행히 오류 없이 원하는 일정만 볼 수 있었다.

 

[결과 수치화]

[수정 전] 500 Internal Server Error 1건 발생

[수정 ] 500 Internal Server Error 0건 발생, 200 OK 메시지 생성 및 조건에 맞추어 일정 목록 및 단건 조회 성공