Study/Project

[프로젝트] 수업 과제물 : SpringBoot 웹 프로젝트 회고록 ①기능 구현

Pearlii 2023. 5. 9. 18:11

개요

Spring, 또는 SpringBoot 둘 중 하나를 택해 웹 서비스를 개발하는 수업의 과제물로 제출한 프로젝트이다.
총 4명의 팀원이 함께 협업하였으며, 수업에서 배운 모든 분야의 지식을 두루 활용하기 위해
프론트/백의 구분을 두지 않고 전원이 풀스택으로 개발하였다.
여기에 팀장으로서 과제 산출물과 전반적인 개발 과정을 검토하고, 회의를 주도하였다.

1편은 기능 구현에 초점을 두어 작성하고, 2편은 테스트 작성과 리팩토링을 다룬다.

 

✨Git 링크

https://github.com/jinju9553/SOMusic-SpringBoot

 

수행 기간

2022.03.15 ~ 2022.06.23

 

기술 스택

  • Language: Java 1.8
  • Framework : SpringBoot, Spirng Data JPA
  • View: JSP, ThymeLeaf
  • DB: Oracle Database
  • Test: JUnit5, Mockito

Spring을 먼저 학습한 후 SpringBoot를 뒤늦게 배웠는데, 시간이 촉박하더라도 SpringBoot로 개발하는 것을 택했다.
JpaRepository 인터페이스를 사용하면 메소드 작명 규칙에 따라 쿼리가 자동으로 생성되고,
Lombok 의 어노테이션을 이용하면 GetterSetter 까지 자동으로 생성되는 등,
Spring보다 다양하고 편리한 기능들이 많이 정의되어 있었기에 한정된 기간 내에 원하는 기능을 모두 개발할 수 있을 거라 판단했다. 겸사겸사 문서를 읽고 라이브러리를 사용하는 요령도 길렀다.

 

✨기능

주요 기능 3가지 정의

 

✨수행 과정

  • 2주차
    • 주요 활동 주제 및 개발범위를 선정하고, 개발 업무를 분담하였다.
  • 3주차
    • 요구사항을 분석하고 세부 기능을 정의하였다.
    • 실제로 구현이 어떤 방식으로 이루어지는지 몰랐기 때문에 요구사항을 과도하게 세분화하는 불상사가 발생했다.
    • 결국 최종 보고서 제출 시에 세분화 된 몇몇 기능을 통합해서 도표를 작성하기도 했다.
  • 4주차
    • UI를 설계하고 Request 처리 흐름도를 간략하게 작성하였다.
    • 요청 흐름 작성은 Spring 기반 웹 프로그램에서 어떤 방식으로 요청이 발생하고 어떤 흐름으로 요청이 전달되는 지 정확하게 알아야만 작성할 수 있는 도표이다. 처음 MVC 구조를 학습할 때에는 쉽사리 구조가 파악되지 않아 교안을 반복해서 확인하기도 했다. 후에는 이 지식을 확실히 내것으로 만들기 위해서 빈 종이 위에 Controller, View, Service 등을 표시하고 그 사이에 오가는 요청을 도표로 그려보기도 하였다. 이 방식이 특히 프로그램의 흐름을 익히기에 적합한 것 같다.
  • 5주차
    • 데이터베이스 스키마(논리적, 물리적)를 설계하고 도메인 클래스를 작성하였다.
    • DB 스키마는 설계 단계에서부터 신중을 기해야 한다고 생각한다. 일전의 프로젝트에서는 각 테이블 별 연관 관계 특성 상 데이터가 꼬이는 바람에, 요구사항에 맞는 API를 설계하기 위해서 DB를 지우고 다시 설계해야만 하는 불상사가 발생한 적 있다.
    • 이 과정에서 특히나 도움이 되는 것은 요청 흐름과 데이터의 이동을 말로 풀어서 설명하는 것이다. DB 스키마는 현실 세계에 존재하는 문제 상황을 데이터로 모델링한 산출물이라고 생각한다. 이 산출물을 올바르게 내기 위해서는 요청 흐름과 요구사항을 정확하게 파악해야 하며, 데이터가 어디에서 어떻게 전달되어야 하는지를 사람의 언어로 잘 풀어내는 것이 요청 흐름과 프로그램 구조를 이해하는 데에 도움이 된다.
    • 이 때문에 팀원들과 온라인 화상 회의에서 몇 시간동안을 말로 설명하고 이해한 것을 DB 구조로 옮겨 적었던 기억이 난다.
  • 6주차
    • 설계한 데이터베이스 스키마를 토대로 ER 다이어그램을 작성한다.
    • 설계서 제출을 위해 개체관의 관계와 각 컬럼의 의미를 풀어서 설명하였다. 상기한 바와 같이 설계서도 결국 사람이 읽는 것이기 때문에 명료하고 이해하기 쉬운 언어로 내용을 설명하는 것이 중요하다고 생각한다. 팀 내의 어떤 사람이 읽어도 중의적으로 해석되지 않고, 한 가지 의미로만 뚜렷하게 설명되어야 하지 않을까 싶다.
  • 7주차
    • Controller 클래스, Service 클래스를 정의하고 Request 처리 흐름도를 최종적으로 작성하였다.
    • 요청 처리 흐름은 Use Case별로 구분하여 작성하였다. 마찬가지로 흐름도를 명료하게 작성하기 위해서라면 프로그램의 실행 구조에 대한 이해가 수반되어야 한다.
    • Controller에서 어떤 handler 메소드를 거쳐야 하는지, 그 메소드에서는 어떤 Service 메소드를 호출하는지, 그 Service 메소드는 또 어떤 DAO 메소드를 호출하고, DAO 메소드는 DB에 접근하여 어떻게 데이터를 처리하고 어떤 데이터(View 또는 Model)를 반환하는지 등등... 수업에서 제공한 예제 프로젝트와 예제의 설계도를 반복적으로 읽으며 분석하였다.
  • 9주차~10주차
    • 중간고사를 마치고 Handler 메소드의 이름을 정의하고, DAO 인터페이스 및 Controller 클래스를 다이어그램으로 정의하였다.
    • 슬슬 산출물이 쌓이는 단계이기도 하고, 설계를 토대로 실질적으로 기능 구현에 들어서는 시기였다. 어떤 클래스를 얼마나 정의하고, 그 안에는 무슨 메소드가 정의되어 있는지를 미리 설계했기 때문에 이를 토대로 개발을 시작하기가 비교적 수월했다.
  • 11주차
    • 전체 기능 중 먼저 구현해야 하는 우선순위를 정하고, 커밋메시지 양식을 통일하였다.
    • 220512 login fix와 같이 yyMMdd + 기능 설명(한글or영어) 으로 통일하였는데, 당시에는 흔히 통용되는 커밋 메시지 양식을 잘 몰라서(feat, fix, style 등등) 이와 같은 형식으로 결정했었다. 조금 더 검색해보고 결정했을걸 하는 아쉬움이 남는다.
  • 12~16주차
    • 본격적으로 기능을 구현해나갔다. 맞닥뜨린 오류와 해결 방법은 아래의 항목에 적는다.

 

✨문제 해결

  • 스프링부트에서 jsp 파일을 인식할 수 없었던 오류
    • application.properties 파일에 경로가 올바르게 설정되어 있는지 검토한다. thymeleaf로 정의된 View는 대개 classpath:templates 디렉토리 아래에 배치되지만, jsp 파일은 그렇지 않다. 본 프로젝트에서는 jsp 파일이 WEB-INF/jsp 디렉토리 아래에 위치하기 때문에 적절하게 파일 경로를 나타내는 prefix와 suffix를 수정해주었다.
  • SQL: 인덱스에서 누락된 IN 또는 OUT 매개변수
    • 질의문과 여기에 매핑되는 Object의 컬럼수가 맞지 않을 때 발생한다. 본 프로젝트에서는 JpaRepositoryspringframework 라이브러리에 정의된 @Repository 어노테이션으로 자동 생성되는 쿼리문 외에도 특정 기능 구현을 위해 JdbcTemplate 기반으로 직접 쿼리문을 작성할 필요가 있었다. 요청하는 데이터의 가짓수가 많을 수록 혼동할 확률이 높으니 쿼리문과 Object가 알맞에 매핑되었는지 검토할 필요가 있다.
  • ORA-00911: 문자가 부적합합니다
    • SQL문 중간에 세미콜론(;)이 포함되는 등, SQL문 자체에 문제가 있을 때 발생하는 오류이다. 오탈자가 없는지 점검하였다.
  • class [I cannot be cast to class [Ljava.lang.Object; ([I and [Ljava.lang.Object; are in module java.base of loader 'bootstrap')
    • ModelAttributeint[] 타입 객체를 전송할 때 발생하는 에러이다. ModelAttribute로는 String[] 또는 Object[] 타입만 전송할 수 있다.
    • ModelAttribute로는 primitive type을 보낼 수 없음에 유의해야 한다.
  • ORA-02296: 사용으로 설정 불가 - 널 값이 발견되었습니다.
    • DB에서 null값으로 생성된 컬럼의 디폴트를 not null로 설정할 때 발생하는 오류이다. String의 경우 공백문자 등을 이용해 초기화 시키고 값을 바꿔주어야 한다.
  • Neither BindingResult nor plain target object for bean name“000”available as request attribute
    • 직역하면 Request Attribute로 사용할 "000"이라는 Bean이 BindingResult나 plain target object로 전송되지 않았음.
    • 즉, View로 보내는 Request에 command 객체를 담지 않아서 발생하는 문제이다. 누락된 Attribute가 없는지 점검한다.
  • java.sql.SQLException: 부적합한 열 인덱스 오류
    • 쿼리문의 파라미터 개수와 거기에 삽입하는 값의 개수, 또는 위치가 달라서 발생하는 오류이다. 오탈자가 없는지 점검한다.

 

✨소감

  • SpringBoot를 이용해 두 번째로 개발해보는 웹 프로젝트인데, Spring의 DI나 IoC등의 원리를 학습한 것은 처음이다.
  • 아직 프레임워크의 동작에 익숙하지 않기 때문에, 데이터베이스와 연동된 기능을 구현하고, CRUD 기능이 온전히 작동하는 프로그램을 만드는 데에 초점을 두어 학습하였다. 
  • 그렇기 때문에 주로 프레임워크를 처음 사용한다는 점에서 비롯된 어려움이나 문법 오류 등이 발목을 잡았다.
  • 또, 과제 제출에 초점을 맞추어 학습하다보니 코드의 질이나, Spring의 작동 원리에 대해서는 크게 고려하지 못했던 점이 아쉽다.
  • 요청 처리 흐름을 파악하기 어렵거나, 프로그램의 구조를 이해하지 못했을 때에는 상기한 바와 같이 그림을 그려 이해하려고 했다.
  • 개발 시간이 넉넉하지 않은 탓에, 구현 중 맞닥뜨린 어려움을 정공법으로 해결하지 못하고 임시방편으로 기능만 돌아가도록 만든 것이 큰 아쉬움으로 남았다.
  • 그 탓에 코드가 난잡해지고 직관적으로 이해하기 어려울 뿐더러, 불필요한 코드 반복이 나타나기도 했다. 이 점은 다음 포스팅인 리팩토링 편에서 해결 방안을 다룰 것이다.