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

31_약수의 개수와 덧셈_일정관리 과제 Lv 1,2 구현완료_25.1.24(금)

codingTrip 2025. 1. 24. 21:11

코트카타

33) 약수의 개수와 덧셈

나의 풀이

class Solution {
    public int solution(int left, int right) {
        int answer = 0;
        
         int i = left;
            
            while (i <= right) {
                int count = 0;
                int j = 1;
                while (j <= i) {
                    if (i % j == 0) {
                        count++;
                        j++;
                    } else {
                        j++;
                    }
                }
                if (count % 2 == 0) {
                    answer += i;
                } else {
                    answer -= i;
                }
                i++;
            }
        return answer;
    }
}

Q1) left ~ right 계속 숫자가 ++되어야 함

-> int i = left; 변수로 선언하여 while문 안에서 i++하게 함

 

Q2) 약수 구하는 방법?

- 소인수 분해? -> 소수를 구하는 방법이 더 어려울 듯

- i % j == 0인 경우의 값을 배열이나 리스트에 넣어서 최종적으로 해당 배열이나 리스트의 length, size를 구할까?

  -> 그러면 각 숫자에 해당하는 배열과 리스트를 계속 생성해야 함...

- count를 이용해서 count % 2 ==0인 경우에 더하고, 아닌 경우에는 빼자

 

=> 중첩 while문을 사용

1부터 자신의 수가 나올 때까지 반복해서 나누고 그 나머지가 0인 경우에만 count++ 했다.

 

다른 분들의 풀이

class Solution {
    public int solution(int left, int right) {
        int answer = 0;

        for (int i=left;i<=right;i++) {
            //제곱수인 경우 약수의 개수가 홀수
            if (i % Math.sqrt(i) == 0) {
                answer -= i;
            }
            //제곱수가 아닌 경우 약수의 개수가 짝수
            else {
                answer += i;
            }
        }

        return answer;
    }
}

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

제곱근을 사용하셨다.

훨씬 간편한 방법인 것 같다.

가끔은 코딩 실력보다... 수학을 더 잘 알아야 할 것 같다는 생각이 든다.


일정관리 과제

필수

Lv 1. 일정 생성 및 조회 - 트러블 슈팅

전체 일정 조회(등록된 일정 불러오기)

다음 조건을 바탕으로 등록된 일정 목록을 전부 조회
 - 수정일 (형식 : YYYY-MM-DD)
 - 작성자명
조건 중 한 가지만을 충족하거나, 둘 다 충족을 하지 않을 수도, 두 가지를 모두 충족할 수도 있습니다.

위의 조건에 대해 잘 이해하지 못해서 선택 일정 조회부터 구현해보기로 했다.

 

선택 일정 조회(선택한 일정 정보 불러오기)

아래와 같은 예외가 발생했다.

2025-01-24T13:31:10.865+09:00 WARN 4241 --- [schedule] [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingPathVariableException: Required URI template variable 'id' for method parameter type Long is not present]

@PathVariable에서 ()로 url 값을 할당해주지 않았기 때문에 발생한 것으로 보인다.

출처 : https://wanggonya.tistory.com/38

@GetMapping("/{scheduleId}")
public ResponseEntity<ScheduleResponseDto> findScheduleById(@PathVariable("scheduleId") Long id){
    return ResponseEntity.ok(scheduleService.findScheduleById(id));
}

 

아래와 같이 200 OK로 잘 나오지만

id, createdAt, updatedAt이 null로 나온다.

ResponseDto와 Mapping 관련 문제인 것으로 보인다.

Postman으로 GET 실행 결과

 

public ScheduleResponseDto(Schedule schedule) {
        this.id = schedule.getId();
        this.todo = schedule.getTodo();
        this.writer = schedule.getWriter();
        this.createdAt = schedule.getCreatedAt();
        this.updatedAt = schedule.getUpdatedAt();
    }

ScheduleResponseDto 생성자에서 id를 설정하지 않았다.

따라서 위처럼 수정했다.

 

JdbcTempleteScheduleRepositoryImpl 에서 아래와 같이 설정했다.

LocalDateTime.parse(rs.getString("createdAt"))

그랬더니 아래와 같은 오류가 발생했다.

java.time.format.DateTimeParseException: Text '2025-01-23 19:18:59' could not be parsed at index 10

튜터님께 도움을 요청했고,

원인은 자바의 LocalDateTime 타입(2025-01-23T19:18:59)과

MySql의 DateTime 타입(2025-01-23 19:18:59)의

각각 형식이 달라서였다.

LocalDateTime now = LocalDateTime.now();
schedule.setCreatedAt(now); // 작성일 설정
schedule.setUpdatedAt(now); // 수정일 설정

위와 같이 설정해도, DB에는 DateTime 형식으로 들어오기 때문에 format해줘야 한다.

 
private RowMapper<Schedule> scheduleRowMapper() {
        return new RowMapper<Schedule>() {
            @Override
            public Schedule mapRow(ResultSet rs, int rowNum) throws SQLException {
                // 문자열을 LocalDateTime으로 변환
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

                return new Schedule(
                        rs.getLong("id"),
                        rs.getString("todo"),
                        rs.getString("writer"),
                        LocalDateTime.parse(rs.getString("createdAt"),formatter),
                        LocalDateTime.parse(rs.getString("updatedAt"),formatter)
                );
            }
        };
    }

 

Postman으로 GET 실행 결과

이제는 위와 같이 잘 나오는 것을 볼 수 있다.

 

 

Lv1 전체 일정 조회(등록된 일정 불러오기)

위의 조회 부분 구현은 일단 두고 다음으로 넘어가기로 했다.

 

Lv 2. 일정 수정 및 삭제 - 트러블 슈팅

선택한 일정 수정

먼저 수정기능 구현이므로 PUT vs PATCH 중에서 고민하기 시작했다.

조건에는 일정, 작성자 즉, 일부만 수정할 수 있도록 구현해야 해서

PATCH를 선택했다.

Postman으로 PATCH 실행 결과

위와 같이 실행하니 400 오류가 발생했다.

 

@Override
public int updateSchedule(Long id, String todo, String writer, String password) {
    return jdbcTemplate.update("UPDATE schedule SET todo = ?, writer = ?, updatedAt = NOW()" +
            "WHERE id = ? AND password = ?;",todo, writer, id, password);
}

원인은 위의 sql문에서 볼 수 있듯이, 비밀번호를 같이 입력해야 하는데 입력하지 않아서 발생한 오류였다.

 

Postman으로 PATCH 실행 결과

비밀번호까지 입력하니 일정, 작성자, 그리고 수정일이 수정 당시 날짜과 시간으로

잘 수정된 것을 확인할 수 있다.

 

Lv1 전체 일정 조회(등록된 일정 불러오기)

다음 조건을 바탕으로 등록된 일정 목록을 전부 조회
 - 수정일 (형식 : YYYY-MM-DD)
 - 작성자명
조건 중 한 가지만을 충족하거나, 둘 다 충족을 하지 않을 수도, 두 가지를 모두 충족할 수도 있습니다.

위의 조건에 대해 이해가지 않는 부분이 있어서

튜터님께 문의를 드리고 답변을 받았다.

select *
from schedule
order by updatedAt desc

select *
from schedule
where DATE(updatedAt)=?
order by updatedAt desc

select *
from schedule
where writer=?
order by updatedAt desc

select *
from schedule
where DATE(updatedAt)=? AND  writer=?
order by updatedAt desc

위의 sql문처럼 크게 4가지의 경우를 가지고

JdbcTempleteScheduleRepositoryImpl에서 각 조건에 맞는 쿼리문을 넣어주는 방법을 제안해주셨다.

 

password = null

일정 작성, 수정, 조회 시 반환 받은 일정 정보에 비밀번호는 제외해야 합니다.

라는 조건에 의해 해당 부분 수정이 필요했다.

Postman으로 GET 실행 결과

public ScheduleResponseDto(Schedule schedule) {
        this.id = schedule.getId();
        this.todo = schedule.getTodo();
        this.writer = schedule.getWriter();
        this.createdAt = schedule.getCreatedAt();
        this.updatedAt = schedule.getUpdatedAt();
    }

ScheduleResponseDto에 위의 코드처럼 생성자로 비밀번호를 제외하고 값을 설정해주었다.

이 타입으로 반환하려고 했는데 컴파일 오류가 발생했다.

 

원인은 선택 일정 조회에 사용했던 RowMapper<Schedule> 타입의 메서드를 호출하려고 해서였다.

타입이 맞지 않아서 아래와 같이 타입에 맞는 새로운 메서드를 만들었다.

 private RowMapper<ScheduleResponseDto> scheduleRowMapper() {
        return new RowMapper<ScheduleResponseDto>() {
            @Override
            public ScheduleResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
                // 문자열을 LocalDateTime으로 변환
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

                return new ScheduleResponseDto(
                        rs.getLong("id"),
                        rs.getString("todo"),
                        rs.getString("writer"),
                        LocalDateTime.parse(rs.getString("createdAt"), formatter),
                        LocalDateTime.parse(rs.getString("updatedAt"), formatter)
                );
            }
        };
    }

 

 

전체 일정 조회 가능

Postman으로 GET 실행 결과

작성자로 조회가능

Postman으로 GET 실행 결과

수정일 조회 문제 발생

Postman으로 GET 실행 결과

Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Method parameter 'updatedAt': Failed to convert value of type 'java.lang.String' to required type 'java.time.LocalDateTime'; Failed to convert from type [java.lang.String] to type [java.time.LocalDateTime] for value [2025-01-23]]

원인은 처음에 controller에서 파라미터로 받아온 수정일을 LocalDateTime 타입으로 설정했고,

String 타입과 달라서 오류가 발생했다.

@GetMapping
    public ResponseEntity<List<ScheduleResponseDto>> findAllSchedule(
            LocalDate updatedAt,
            String writer
    ) {
        log.info("updatedAt={}", updatedAt);
        log.info("writer={}", writer);

        return ResponseEntity.ok(scheduleService.findAllSchedules(updatedAt,writer));
    }

 

따라서 아래와 같이 LocalDate 타입으로 매개변수를 받고,

이를 toString()을 통해 String으로 변환해서 이 값을 쿼리문에 넣어주었다.

@Override
    public List<ScheduleResponseDto> findAllSchedules(LocalDate updatedAt, String writer) {

        String sql;
        String strUpdateAt = updatedAt.toString();

        if (updatedAt != null && writer == null){
            sql = "select * from schedule where DATE(updatedAt)= ? order by updatedAt desc";
            return jdbcTemplate.query(sql,scheduleRowMapper(),strUpdateAt);
        }

        if (updatedAt == null && writer != null){
            sql = "select * from schedule  where  writer=? order by updatedAt desc";
            return jdbcTemplate.query(sql,scheduleRowMapper(),writer);
        }

        if (updatedAt != null && writer != null){
            sql = "select * from schedule where DATE(updatedAt)=? AND writer=? order by updatedAt desc";
            return jdbcTemplate.query(sql,scheduleRowMapper(),strUpdateAt,writer);
        }

        sql = "select * from schedule order by updatedAt desc";

        return jdbcTemplate.query(sql,scheduleRowMapper());
    }

 

Postman으로 GET 실행 결과

 

 

Postman으로 GET 실행 결과

그러나... 이번에는 작성자명만 입력했더니 아래와 같은 예외가 발생했다.

java.lang.NullPointerException: Cannot invoke "java.time.LocalDate.toString()" because "updatedAt" is null

원인은  String strUpdateAt = updatedAt.toString(); 

수정일을 파라미터로 받아오지 않을 경우 위와 같이 하면 예외가 발생했다.

따라서 아래와 같이 각 조건에 해당하는 경우에만 String으로 변환해주었다.

    @Override
    public List<ScheduleResponseDto> findAllSchedules(LocalDate updatedAt, String writer) {

        String sql;

        if (updatedAt != null && writer == null){
            sql = "select * from schedule where DATE(updatedAt)= ? order by updatedAt desc";
            return jdbcTemplate.query(sql,scheduleRowMapper(),String.valueOf(updatedAt));
        }

        if (updatedAt == null && writer != null){
            sql = "select * from schedule  where  writer = ? order by updatedAt desc";
            return jdbcTemplate.query(sql,scheduleRowMapper(),writer);
        }

        if (updatedAt != null && writer != null){
            sql = "select * from schedule where DATE(updatedAt)=? AND writer=? order by updatedAt desc";
            return jdbcTemplate.query(sql,scheduleRowMapper(),String.valueOf(updatedAt),writer);
        }

        sql = "select * from schedule order by updatedAt desc";

        return jdbcTemplate.query(sql,scheduleRowMapper());
    }

 

 

Postman으로 GET 실행 결과

처음 과제 발제를 들었을 때에는

막막하기만 했고

필수 과제도 못할 것 같아서 두려웠는데...

 

그래도 오늘은 필수 과제 구현은 다했으므로

마음이 한결 놓인다.