본격적인 Spring 주차를 시작하고 처음 겪은 과제 일정 관리 앱 만들기,
애초에 조금 시간이 빠듯하기도 했다고 느꼈지만 강의를 빠르게 듣고 일단은 바로 부딪혀보는게 답일 것 같았다.
그리고 간신히 당일날에서야 마무리한 이번 과제
정말정말정말 많은 에러들과 마주쳤다.
대부분은 작은 오타나 설정 실수였지만, 그 원인을 찾는 과정에서 배운 게 정말 많다.
이번 과제 동안 겪었던 주요 문제들과 해결 방법을 정리해본다.
1. 프로젝트 이름과 패키지 이름을 통일하지 못해서 헤맴
처음에 Entity를 설계할 때는 패키지를 com.example.scheduler, 클래스 이름을 Scheduler로 잡기로 했다. 그런데 실제로 IntelliJ에서 폴더를 만들 때는 프로젝트 이름이 schedule였던 영향으로 나도 모르게 com.example.schedule, Schedule로 r을 떼고 만들어버렸다. 이때라도 빠르게 문제를 파악했어야 하는데...
Entity를 import할 때 계속 Schedule(r 없이)로 잘못 쓰니, 컴파일 에러가 났다. 몇 번을 반복하고 나서야 프로젝트 전체를 통일했다.
이름 하나를 프로젝트 초반에 확실히 정하고 시작하지 않으면, 뒤로 갈수록 그 대가를 계속 치르게 된다는 걸 배웠다.
2. import 문법을 잘못 써서 컴파일 자체가 안 됨
ScheduleResponseDto 파일에서 error: <identifier> expected 라는 에러가 났다. 원인을 찾아보니 import 문에서 실제로 존재하지 않는 클래스 이름을 쓰고 있었다. 패키지는 Scheduler로 통일했는데 import 구문에는 여전히 Schedule을 쓰고 있었던 것.
import 에러는 메시지만 보면 뭐가 문제인지 잘 모른다.
"identifier가 필요하다"는 말이 정확히 어떤 걸 가리키는지 처음엔 이해하지 못했다.
결국 import 줄을 하나하나 실제 파일 이름과 대조해보고서야 발견했다.
3. 따옴표 하나가 없어서 어노테이션이 깨짐
너무 기초적인 오타 에러라서 이걸 써야하나 싶기도 하지만..
@Table(name = "schedulers") 라고 써야 하는데 뒤쪽 따옴표가 하나 빠져서 @Table(name = schedulers") 가 되어있었다. 빨간 줄이 뜨긴 했는데 정확히 어디인지 처음엔 못 찾았다. 여러 복잡한 언어와 알록달록한 색깔 속에서 고작 " 이 점 두개를 찾는 것은 결코 쉽지가 안핟. 결국 에러 메시지를 직접 확인하고, 다시 타이핑해서 해결했다. .
4. 필요 없는 BaseEntity를 상속받으려다 꼬임
Entity 코드를 짜다가 extends BaseEntity 라는 걸 썼는데, 정작 BaseEntity라는 클래스를 만든 적이 없었다. 아마 따라치기 과제에서 아이디어를 얻으려고 하다보니 생각없이 써제껴 내려간 부분이 원인..
이번과제는 BaseEntity 같은 공통 상속 구조가 필요 없었다.
extends BaseEntity를 삭제해 정리했다.
이 경험으로 배운 건, 필요하지도 않고 자아없이 고급 패턴을 따라 쓰기만하면 오히려 이해도 못한 채 에러만 늘어난다는 것이다.
지금 단계에서 진짜 필요한 게 뭔지 판단하고 그것만 쓰는 게 중요했다.
5. 클래스 선언과 생성자를 혼동함
DTO의 생성자를 짜다가 이렇게 써버린 적이 있다.
public class ScheduleRequestDto{String title, String content, String author, String password){
여기엔 두 가지 문제가 겹쳐있었다. 하나는 class 키워드를 지우지 않아서 클래스 안에 또 클래스를 선언하는 것.
다른 문제는 중괄호와 소괄호를 헷갈려서, 매개변수를 감싸야 할 자리에 중괄호를 쓴 것.
생성자는 클래스가 아니라 메서드처럼 생긴 것이고, class 키워드 없이 클래스 이름만 그대로 쓴다는 것, 그리고 매개변수는 항상 소괄호로 감싼다는 걸 이 실수를 통해 익혔다.
6. final 필드와 생성자 방식을 두고 스타일이 갈라짐
DTO를 만들 때 나는 필드를 final로 선언하고 생성자를 직접 하나만 만드는 방식으로 짰다. 이렇게 하면 한번 생성되면 값이 절대 안 바뀌는 불변 객체가 된다는 설명을 들었다. 다만 final을 쓰면 Lombok의 @NoArgsConstructor나 @AllArgsConstructor는 같이 쓸 수 없다는 것도 알게 됐다. final 필드는 반드시 생성 시점에 값이 채워져야 하는데, 빈 생성자는 그걸 보장할 수 없기 때문이다.
이 스타일 차이 자체는 정답이 있는 게 아니라 설계 선택의 문제였지만, 왜 그렇게 짰는지 이유를 몰랐다면 나중에 다른 코드와 섞여서 혼란스러웠을 것 같다.
결국 내 자아를 갖고 의도와 이유를 알고 코드를 짜나아가야 해결방법도 내가 이해하고 수용할 수 있다.
7. DTO 필드 이름이 Entity와 미묘하게 달라 나중에 헷갈릴 뻔함
ScheduleResponseDto를 짤 때 필드 이름을 contents, createAt이라고 썼는데, 정작 Entity 쪽은 content, createdAt이었다.
이름을 Entity와 똑같이 맞추는 게 좋을 것 같아 content, createdAt으로 통일했다.
내가 만든 코드여도 내가 모든 것을 기억할 수가 없기 때문에 결국엔 흐름을 잃지 않기 위해 메모하거나 필기하는 습관을
들이는 것이 좋을 것 같다.
8. ResponseDto에 password 필드를 남겨둠
가장 중요한 실수.
ScheduleResponseDto에 private final String password 필드를 추가하고 생성자에도 넣었다.
이러면 클라이언트에게 응답을 보낼 때 비밀번호가 그대로 노출된다. 발제에는 "Response에는 비밀번호를 넣지 않는다"고 정해져있으나, 실제로는 장시간 코드를 짜다 보니 집중력도 떨어지고 길을 헤메대가 결국 습관적으로 넣어버린 것이다.
이 문제는 컴파일 에러(생성자 매개변수 순서가 꼬여서 타입이 안 맞는 에러)로 나타났는데, 원인을 파고들어보니 설계 원칙을 근본 원인이었다. 에러 메시지만 보고 타입만 맞추는 게 아니라, 애초에 이 필드가 여기 있어야 하는지부터 다시 스스로에게 되물어봐야 한다는 걸 배웠다.
9. Guestbook 예시를 그대로 베끼려다 오히려 막힘
결국 이 과제를 진행하면서 거의 모든 문제를 일으켰던 근본적인 실수들
메서드를 짤 때, 참고하던 따라치기 예시 코드의 변수 이름과 타입(CreateGuestbook, createGuestbook.getName() 등)을 그대로 흉내내려다가, 정작 이번 과제에는 존재하지도 않는 타입과 메서드를 쓰는 코드를 넣어버렸고
그게 어디서부터 어떻게 문제가 됐는지 손쓸수도 없고 멘붕에 빠져버렸다.
결국 모방을 하려고 해도 패턴을 이해하지 못한 채 표면적으로만 베끼면 이런 일이 생기는것이다.
예시 코드를 보되 "이 부분이 현재 진행하고 있는 프로젝트에서는 무엇에 해당하는지"를 하나하나 짚어가며 옮겨야 한다.
9-1. Repository와 Service 사이에서 메서드 이름이 서로 다름
Entity에는 updateSchedule이라는 메서드를 만들어뒀는데, Service에서는 scheduler.update(...)라고 다른 이름으로 호출해서 에러가 났다. 또 매개변수도 Entity 메서드는 title과 author를 받도록 만들었는데, Service에서는 title과 content를 넘기려고 했다. 이름과 매개변수 둘 다 맞춰야 컴파일이 통과된다는 걸 여러 번의 시행착오 끝에 체감했다.
10. import java.util.List 하나가 빠져서 전체가 막힘
이쯤되선 거의 자괴감에 빠졌던 것 같다.
findAll 메서드에서 List<ScheduleResponseDto>를 반환하도록 짰는데, import 문에는 ArrayList만 있고 List는 빠져있었다.
아주 흔한 실수인데, Java에서는 사용하는 모든 타입을 각각 import해야 한다는 점 상기...
11. 변수 타입의 대소문자를 놓침
기초적인 실수. 숫자, 대소문자, 따옴표 등등등 ....
Scheduler updatedScheduler = ... 라고 써야 하는데 scheduler updatedScheduler = ... 로 타입 이름 첫 글자를 소문자로 썼다. Java는 대소문자를 구분하는 언어라서, scheduler라는 소문자 타입은 존재하지 않으니 바로 에러가 났다.
타입은 대문자로 시작한다를 지키지 않으면 이런 사소한 오타가 잘 발생한다
12. 중괄호 하나가 남거나 모자라서 클래스가 중간에 끊김
기본적인 문법 실수!!!!!!!
Service의 updates 메서드 에서 비밀번호 검증 후 실제 로직이 if 블록 안에 갇히는 상황이 생겼다.
원인은 메서드를 닫는 중괄호 아래에 여분의 닫는 중괄호가 하나 더 있었다.
때문에 SchedulerService 클래스 자체가 중간에 닫서, 그 아래 있던 delete와 getOrThrow 메서드가 클래스 바깥에 떨어져 있는 상태가 됐다.
중괄호 문제는 눈에 띄는 에러 위치와 실제 원인이 되는 위치가 다른 경우가 많아서 정말 찾기 어렵다.
애써 잘 써놓고도 사소한 부분을 놓치고 코드에 문제가 있는줄 알고 붙들고 있다간 시간과 체력 모두 낭비한다.
13. Controller에서 클래스 이름과 인스턴스 변수 이름을 혼동
Controller의 create 메서드에서 schedulerService.create(...) 라고 써야 하는데 SchedulerService.create(...) 처럼 대문자로 시작하는 클래스 이름을 그대로 썼다.
실제로 필요한 건 필드로 주입받은 소문자 인스턴스인데, 타입 이름과 변수 이름을 혼동한 것이다. 이 실수는 세 번 정도 반복해서 나왔는데, 그때마다 놓쳤다가 나중에야 잡아냈다.
14. Service와 Controller에서 메서드 이름이 서로 다름
Service에는 updates라는 이름으로 메서드를 만들어놨는데, Controller에서는 update(끝에 s 없이)로 호출하고 있었다.
이름이 정확히 일치하지 않으면 컴파일러가 그런 메서드를 찾을 수 없다며 에러 발생...
결국 Service 쪽 이름을 update로 통일해서 맞췄다.
15. delete 메서드가 비밀번호를 받지 못하는데 비밀번호를 검증하려 함
과제 요구사항에서 삭제할 때도 비밀번호 확인이 필요했는데,
처음 짠 delete(Long id) 메서드는 id만 받고 있었다.
그런데 메서드 안에서는 requestDto.getPassword()를 쓰려고 해서, 존재하지도 않는 변수를 참조하는 에러가 났다.
매개변수를 delete(Long id, ScheduleRequestDto requestDto)로 바꿔서 비밀번호 정보를 받을 수 있게 고쳤고,
Controller의 delete 메서드에도 @RequestBody로 body를 받는 부분을 추가했다.
16. application.properties 파일이 비어있음
Entity, OTD, Controller, service등 기능들을 구현하고 이제는 끝났다 라고 생각하고
서버를 실행하려고 하니 "Failed to determine a suitable driver class"라는 에러가 떴다.
정말 이땐 소리를 지르고 싶었다.
application.properties 파일을 열어보니 설정(datasource url, username, password)이 전부 사라져 있었다.
아마 프로젝트를 여러 번 리셋하면서 파일이 초기화됐던 것 같다.
이걸 고치면서 깨달은 건, 설정 파일은 프로젝트를 진행하면서 처음 한 번만 설정하고 잊기 쉽다는 것.
특히 작은 부분이라서 빌드 에러가 나도 설정을 의심 안 한다.
이제부턴 "컴파일은 되는데 런타임 에러가 나면 설정을 먼저 확인하자" 고 생각했다.
17. URL 문자열에 오타 (mysqul vs mysql)
application.properties 파일을 복구해서 서버를 다시 실행하니 이번엔 "Driver com.mysql.cj.jdbc.Driver claims to not accept jdbcUrl, jdbc:mysqul://localhost:3306/scheduler"라는 에러가 떴다. (이땐 그냥 한숨이 나왔던 듯..)
에러 메시지를 자세히 읽어보니 mysqul이라고 되어있었다. mysql을 mysqul로 쳤던 거다.
당연하게도 단 한 글자 차이인데 인식은 못 한다..
프로그래밍은 정말 한 글자도 틀리면 안 되는 작업이라는 걸 다시 한번 깨달았다. 한 순간의 실수때문에 성격 진짜 많이 버리는 듯..
배운 점
이렇게 나열해보니 스무 개에 가까운 문제를 만났는데, 그중에 정말로 어려운 로직 문제는 거의 없었다. 대부분은 이름이 안 맞거나, 괄호나 따옴표 위치가 틀렸거나, import가 빠졌거나, 파일 안의 값이 사라졌거나 하는 것들이었다. 그런데도 이런 문제들이 실제로는 가장 많은 시간을 잡아먹었다.
여기서 얻은 가장 큰 교훈은, 에러 메시지가 가리키는 위치가 항상 진짜 원인이 있는 위치는 아니라는 것이다. 중괄호 하나가 엉뚱한 곳에 있으면 전혀 상관없어 보이는 곳에서 에러가 난다. 그래서 에러 메시지를 그대로 믿기보다, 그 메시지가 나올 수 있는 다양한 원인을 하나씩 좁혀가는 태도가 필요했다.
또한 이름을 짓는 규칙(Entity는 단수형, Repository와 Service는 Entity 이름을 따라간다 등)을 프로젝트 초반에 확실히 정해두지 않으면, 그 대가를 프로젝트 내내 여러 번 나눠서 치르게 된다는 것도 깨달았다. 작은 일관성이 나중의 큰 시간을 아껴준다.
다음 프로젝트에서는 이름과 타입을 먼저 확실히 설계하고, 예시 코드를 참고할 때도 표면적으로 베끼기보다 각 부분이 우리 프로젝트에서 무엇에 대응하는지 짚어가며 옮기는 습관을 들이고 싶다.
18. DTO 타입을 잘못 받음
Controller의 create 메서드를 작성할 때, 반환 타입과 변수 타입이 섞였다.
ScheduleResponseDto saved = SchedulerService.create(ScheduleRequestDto);
여기서 두 가지 실수가 있었다.
첫째, SchedulerService클래스처럼 대문자로 썼는데, 실제로 필요한 건 필드로 주입받은 schedulerService(소문자) 객체다.
둘째, 메서드를 호출할 때 타입 이름만 쓰고 실제 변수를 안 넣었다.
타입 선언은 정의할 때만 필요하고, 호출할 때는 변수 이름을 써야 한다는 기본을 다시 상기시켰다.
.
배운 점
이 과제를 진행하면서 깨달은 가장 큰 점은, 코드의 논리가 틀린 경우보다 작은 실수(오타, 줄바꿈, 타입 이름)로 인한 에러가 훨씬 더 시간을 빼앗는다는 것이다. 그리고 에러 메시지가 항상 진짜 원인을 직접적으로 알려주지 않고 또한 인텔리제이가 추천하는 방법을 따라갔다가는 오히려 돌이킬 수 없는 강을 건너게 된다는 것도 배웠다.
지금 단계에서는 내 스스로가 자아도 없고 배운것도 없다.
그 때문에 오히려 애매하게 알고서 뭔가를 해보려고 하다가 오류를 내는 경우보단
흐름을 이해하거나 잡지 못한채 코드위에서 표류하다가
사소한 오타하나, 실수 하나, 그리고 그걸 찾지 못해서
또 어떻게 고치는지 몰라서 문제를 만들어내는 것 같다.
그래서 더 중요한 건 에러 메시지를 읽는 능력, 그리고 의심 범위를 체계적으로 좁혀나가는 방법인 것 같다.
또한 이번 경험은 왜 개발자들이 "작은 실수를 잡아내기 위해 코드 리뷰를 하고, 테스트를 쓰고, 여러 번 확인한다"는 말이 통용되는지를 이해하게 해줬다. 프로그래밍은 정확함이 생명이고, 정확함은 습관에서 나온다고 생각한다.
다음 프로젝트에선 이런 실수들을더 빨리 캐치하려고 노력하고 흐름을 이해할 수 있게 노력해야 할 것이다.
'Camp > 과제' 카테고리의 다른 글
| [TIL]일정 관리 앱 Develop (0) | 2026.07.09 |
|---|---|
| [계산기 과제 2차] 트러블슈팅 (0) | 2026.06.23 |
| [Java] 계산기 과제 Lv.2 트러블 슈팅- 클래스 분리가 왜 이렇게 헷갈렸나 (0) | 2026.06.19 |
| [Java] 코드카타 트러블 슈팅 (0) | 2026.06.17 |
| Camp/과제[TIL] CH2 Java 계산기 과제 Lv.2 (트러블 슈팅) (0) | 2026.06.16 |