Skip to content

ppusda/MML

Repository files navigation

🎶 MML (My Music List)

자신만의 음악 플레이리스트를 만들고 공유하기 위한 서비스

📝 기능 소개

  1. 자신만의 음악 플레이리스트를 생성하여 음악을 추가, 삭제 가능

메인 페이지

재생목록 추가 페이지

  1. 음악 플레이리스트를 저장하여 공유 가능
## 혁오 노래 모음 🎵

- 🎵 [혁오 - Gang Gang Schiele](https://www.youtube.com/watch?v=Xjk3w7NcZAU) ▶️
- 🎵 [혁오 - 멋진헛간 Remix](https://www.youtube.com/watch?v=3DpL4UcCdWk) ▶️

🛠 기술 스택

  • Spring Boot 3.3.4 / Kotlin
  • zulu JDK 17
  • Spring Data JPA
  • MySQL
  • Kotlin JDSL

🔗 ERD

MML_ERD

⚙ API

User

GET /v1/members - 회원 조회

Request

{}

Response

{
  "userReponse" : [
    {
      "id" : 1,
      "email" : "ppusda@naver.com"
    },
    {
      "id" : 2,
      "email" : "ppusda1234@gmail.com"
    },
  ]
}
POST /v1/members - 로그인

Request

{
  "email": "string",
  "password": "string"
}

Response

{
  "id": 1,
  "email": "ppusda@naver.com"
}

Music

GET /v1/musics - 음악 조회

Request

{}

Response

{
  "musicResponse" : [
    {
      "id" : 1,
      "title" : "Gang Gang Schiele",
      "artist" : "혁오",
      "url" : "https://www.youtube.com/watch?v=WB4547-tSJA",
    },
    {
      "id" : 2,
      "title" : "멋진헛간 Remix",
      "artist" : "혁오",
      "url" : "https://www.youtube.com/watch?v=3DpL4UcCdWk"
    }
  ]
}
GET /v2/musics - 음악 검색

Request
/v2/musics?keyword="혁오"

Response

{
  "musicResponse" : [
    {
      "id" : 1,
      "title" : "Gang Gang Schiele",
      "artist" : "혁오",
      "url" : "https://www.youtube.com/watch?v=WB4547-tSJA",
    },
    {
      "id" : 2,
      "title" : "멋진헛간 Remix",
      "artist" : "혁오",
      "url" : "https://www.youtube.com/watch?v=3DpL4UcCdWk"
    }
  ]
}
POST /v1/musics - 음악 등록

Request

{
  "title" : "Gang Gang Schiele",
  "artist" : "혁오",
  "url" : "https://www.youtube.com/watch?v=WB4547-tSJA",
}

Response

{
  "id" : 1,
  "title" : "Gang Gang Schiele",
  "artist" : "혁오",
  "url" : "https://www.youtube.com/watch?v=WB4547-tSJA",
}
PUT /v1/musics/{n} - n번 음악 정보 수정

Request

{
  "title" : "멋진헛간 Remix",
  "artist" : "혁오",
  "url" : "https://www.youtube.com/watch?v=3DpL4UcCdWk" 
}

Response

{}
DELETE /v1/musics/{n} - n번 음악 삭제

Request

{}

Response

{}

Playlist

GET /v1/playlists - 플레이리스트 조회

Request

{}

Response

{
  "playlistResponse" : [
    {
      "id" : 1,
      "ownerEmail" : "ppusda@naver.com",
      "name" : "내가 자주 듣는 혁오 노래 모음집"
    },
    {
      "id" : 2,
      "ownerEmail" : "ppusda1234@gmail.com",
      "name" : "혁오"
    }
  ]
}
POST /v1/playlists - 플레이리스트 생성

Request

{
  "name" : "내가 자주 듣는 혁오 노래 모음집"
}

Response

{
  "id" : 1
}
PATCH /v1/playlists/{n} - n번 플레이리스트 정보 수정

Request

{
  "name" : "혁오"
}

Response

{}
DELETE /v1/playlists/{n} - n번 플레이리스트 삭제

Request

{}

Response

{}

Playlist-Music

GET /v1/playlists/{n}/musics - n번 플레이리스트 조회

Request

{
  "playlistMusicResponse" : {
    "id" : 1,
    "ownerEmail" : "ppusda@naver.com",
    "musics" : [
      {
        "id" : 1,
        "title" : "Gang Gang Schiele",
        "artist" : "혁오",
        "url" : "https://www.youtube.com/watch?v=WB4547-tSJA",
      },
      {
        "id" : 2,
        "title" : "멋진헛간 Remix",
        "artist" : "혁오",
        "url" : "https://www.youtube.com/watch?v=3DpL4UcCdWk"
      }
    ]
  }
}

Response

{}
POST /v1/playlists/{n}/musics - n번 플레이리스트에 음악 추가

Request

{}

Response

{}
DELETE /v1/playlists/{n}/musics - n번 플레이리스트에 음악 삭제

Request

{}

Response

{}

🏆 미션 결과물

[미션 1] 테이블 설계하기 제출 스레드

커밋 내용 => Update README.md

[미션 2] 깃허브 리포지토리에 프로젝트 올리기

커밋 내용 => init: initialize project

[미션 3] REST API 설계하기

커밋 내용 => docs: API 설계 - 요청/응답 전체 추가

[미션 4] 조회 REST API 만들기

커밋 내용 => feat: 조회 API 테스트 코드 작성

음악 전체 조회
          
[
  {
    "title": "Gang Gang Schiele",
    "artist": "혁오",
    "url": "https://www.youtube.com/watch?v=Xjk3w7NcZAU"
  },
  {
    "title": "멋진헛간 Remix",
    "artist": "혁오",
    "url": "https://www.youtube.com/watch?v=3DpL4UcCdWk"
  },
  {
    "title": "Happy",
    "artist": "Day6",
    "url": "https://www.youtube.com/watch?v=VXp2dCXYrvQ"
  }
]
          
        
플레이리스트 전체 조회
          
[
  {
    "name": "혁오 노래 모음",
    "musics": [
      {
        "title": "Gang Gang Schiele",
        "artist": "혁오",
        "url": "https://www.youtube.com/watch?v=Xjk3w7NcZAU"
      },
      {
        "title": "멋진헛간 Remix",
        "artist": "혁오",
        "url": "https://www.youtube.com/watch?v=3DpL4UcCdWk"
      }
    ]
  },
  {
    "name": "내가 자주 듣는 노래",
    "musics": [
      {
        "title": "Gang Gang Schiele",
        "artist": "혁오",
        "url": "https://www.youtube.com/watch?v=Xjk3w7NcZAU"
      },
      {
        "title": "멋진헛간 Remix",
        "artist": "혁오",
        "url": "https://www.youtube.com/watch?v=3DpL4UcCdWk"
      },
      {
        "title": "Happy",
        "artist": "Day6",
        "url": "https://www.youtube.com/watch?v=VXp2dCXYrvQ"
      }
    ]
  }
]
          
        
N번 플레이리스트 조회
          
{
  "name": "혁오 노래 모음",
  "musics": [
    {
      "title": "Gang Gang Schiele",
      "artist": "혁오",
      "url": "https://www.youtube.com/watch?v=Xjk3w7NcZAU"
    },
    {
      "title": "멋진헛간 Remix",
      "artist": "혁오",
      "url": "https://www.youtube.com/watch?v=3DpL4UcCdWk"
    }
  ]
}
          
        
[미션 5] 삽입, 수정, 삭제 REST API 만들기

커밋 내용 => feat: [미션5] 삽입, 수정, 삭제 REST API 만들기

테스트 결과

🧩 고도화 결과물

Restful 하도록 API 수정하기

커밋 내용 => refactor: Api 수정 (자원 복수형, 버저닝), refactor: Api 수정 (PUT, PATCH)

PUT, PATCH에 대하여

위 내용은 이전부터 고민하고 사용했다고 생각했지만 코치님께 관련 피드백을 받게 되었다. 그렇게 자료를 좀 더 찾아보게 되었고 아래와 같은 고민과 생각을 마주치게 되었다.

수정이라는 작업을 할 때 PUT을 더 사용하는 이유가 있을까요?
그래서 PUT 이랑 PATCH 는 뭐가 다른건가요

부끄럽게도 나는 단순히 "PUT은 자원의 전체 교체", "PATCH는 자원의 일부 교체" 정도의 개념만 알고 있었기에 문제였던 것을 알게되었다.
아래와 같은 내용을 알아두려고 한다.

  • 언급되는 자원은 Entity의 컬럼들과 동일 시 되지 않는다. / DTO에 명시 한 목록이 자원이 되며, 이 자원들을 모두 교체 하느냐 마느냐가 PUT, PATCH로 갈리게 된다.
  • PUT은 자원의 전체 교체, 입력 받은 모든 데이터가 그대로 반영되어야 한다.
  • PATCH는 자원의 일부 교체, 입력 받은 데이터 중 null이 있더라도 이전 데이터를 유지하며, 입력된 데이터만 반영한다.
  • REST한 API를 항상 지키기는 힘들다. 하지만 차이를 알고 정해놓은 컨벤션에 맞춰서만 작성한다면 크게 문제되지는 않는다.

위와 같은 내용들을 생각하고 코드를 수정했으며, Music의 경우는 PUT으로 변경, Playlist의 경우는 PATCH로 동작 할 수 있도록 코드를 수정하였다.

읽기 좋은 코드로 수정하기 (주석, 메서드 네이밍 수정, 퍼사드 패턴 적용)

커밋 내용 =>

10/30 목 refactor: 읽기 좋은 코드로 수정하기 - Member (+ List -> Page로 수정)
refactor: 읽기 좋은 코드로 수정하기 - Member (누락 및 오타 수정)
refactor: 읽기 좋은 코드로 수정하기 - Member (오타 수정)
refactor: 읽기 좋은 코드로 수정하기 - Member (주석 수정)
refactor: 읽기 좋은 코드로 수정하기 - Music (+ List -> Page로 수정, 테스트 코드 일부 수정)
refactor: 읽기 좋은 코드로 수정하기 - Playlist (+ List -> Page로 수정)
11/01 금 refactor: 읽기 좋은 코드로 수정하기 - PlaylistMusic (+ List -> Page로 수정, 테스트 코드 일부 수정)
refactor: 읽기 좋은 코드로 수정하기 - PlaylistMusic (Operation 추가)
refactor: 읽기 좋은 코드로 수정하기 - 퍼사드 패턴 적용하기 (MusicListRepository)
refactor: 읽기 좋은 코드로 수정하기 - 오타 수정 및 불필요한 import 제거

후기

이번 자체미션에서는 한 번에 이해되지 않는 메서드 네이밍을 수정하거나 구현했던 기능들에 대해 모두 주석을 달아보게 되었다. 처음 작성할 때는 이상한 점을 느끼지 못했는데 다시 보니 이상하다고 느껴지는 메서드 네임들도 있었고, 주석을 달다 보니 누구나 이런 부분에서 벗어나더라도 더 쉽게 이해할 수 있을 것 같다고 생각하게 되었다.

물론 어려운 작업은 아니었지만 생각을 요하는 작업이었던 것 같다.

추가로 퍼사드 패턴을 적용해서 Music, Playlist 간에 발생하는 간단한 DB 로직들을 합쳐서 관리할 수 있게 하였다. 혼자서 직접 적용해보는 건 처음이었지만 어렵지는 않았고, 수정한 코드를 보니 확실히 더 깔끔해졌다고 느꼈다.

복잡한 구조라면 이러한 패턴을 적용해보는 것을 고민해봐야겠다고 생각되었고, 다른 디자인 패턴들도 적용해볼 수 있는지 좀 더 알아봐야겠다.

테스트 커버리지 100% 달성하기(단위 테스트 위주로 작성)

커밋 내용 =>

11/02 토 feat: JaCoCo 설정 추가 (+ 각 요소 설명)
refactor: 테스트 커버리지 100% 달성하기 - 기존 테스트 변경사항 수정
11/03 일 feat: 테스트 커버리지 100% 달성하기 - MemberService 테스트 작성
11/04 월 feat: 테스트 커버리지 100% 달성하기 - MemberService 예외 상황 테스트 작성 (+ Mockito-Kotlin 추가)
11/05 화 fix: 테스트 커버리지 100% 달성하기 - MemberService / 상수로 수정, mockito-kotlin 라이브러리로 수정
feat: 테스트 커버리지 100% 달성하기 - MusicService 테스트 작성
feat: 테스트 커버리지 100% 달성하기 - PlaylistService 테스트 작성
feat: 테스트 커버리지 100% 달성하기 - PlaylistMusicService 테스트 작성
fix: 테스트 커버리지 100% 달성하기 - 불필요한 import 제거 및 디렉토리 정리
11/06 수 refactor: 테스트 커버리지 100% 달성하기 - BaseServiceTest 기본 설정 클래스 추가
refactor: 테스트 커버리지 100% 달성하기 - BaseControllerTest 기본 설정 클래스 추가 (WebMvcTest)
fix: 테스트 커버리지 100% 달성하기 - BaseServiceTest 접근제어자 및 명명규칙 준수
11/07 목 fix: 테스트 커버리지 100% 달성하기 - 기존 테스트에 변경사항 적용
11/08 금 feat: 테스트 커버리지 100% 달성하기 - BaseRepositoryTest 기본 설정 클래스 추가
11/09 토 feat: 테스트 커버리지 100% 달성하기 - PlaylistMusicRepository / Custom 메서드 테스트
feat: 테스트 커버리지 100% 달성하기 - 주석 정리
11/10 일 feat: 테스트 커버리지 100% 달성하기 - PlaylistMusicRepository 테스트 작성
fix: 테스트 커버리지 100% 달성하기 - Entity 미사용 Setter 제거
fix: 테스트 커버리지 100% 달성하기 - 테스트 공통 사항 적용 및 Excpetion 검증
fix: 테스트 커버리지 100% 달성하기 - 업데이트 로직 수정
11/11 월 feat: 테스트 커버리지 100% 달성하기 - ApiResponse, Custom Exception 테스트 작성
feat: 테스트 커버리지 100% 달성하기 - DTO 테스트 작성 (+ Member DTO 경로 수정)
feat: 테스트 커버리지 100% 달성하기 - MmlApplication main @generated로 제외
feat: 테스트 커버리지 100% 달성하기 - Entity 내 수정 메서드 테스트 작성

테스트 커버리지 - 초기

테스트 커버리지 - 초기

테스트 커버리지 - 기존 테스트 변경사항 적용 후

테스트 커버리지 - 기존 테스트 변경사항 적용 후

테스트 커버리지 - 멤버 추가 후

테스트 커버리지 - 멤버 추가 후

테스트 커버리지 - 서비스 추가 후

테스트 커버리지 - 서비스 추가 후

테스트 커버리지 - 컨트롤러 변경사항 적용 후

테스트 커버리지 - 컨트롤러 변경 후

테스트 커버리지 - 리포지토리 추가 후

테스트 커버리지 - 리포지토리 추가 후

테스트 커버리지 - 미사용 Setter 제거 및 Exception 검증 로직 등 변경 후

테스트 커버리지 - 미사용 Setter 제거 및 Exception 검증 등 변경사항 적용 후

테스트 커버리지 - Controller Advice, Custom Excpetion 테스트 추가 후

테스트 커버리지 - Controller Advice, Custom Excpetion 테스트 추가 후

테스트 커버리지 - DTO 테스트 추가 후

테스트 커버리지 - DTO 테스트 작성

테스트 커버리지 - Entity 내 메서드 테스트 추가 및 메인 메서드 제외 후

테스트 커버리지 - Entity 메서드 테스트와 Main 메서드 제외

후기

꽤 오랜시간을 투자해서 JaCoCo 테스트 커버리지 100%를 달성하게 되었다.
총 84개의 테스트와 24개의 파일을 만들었으며 코치님께서 말씀하셨던 커버리지 100%를 달성해보면 배우는 것이 있을 것이라는 말에 공감하게 되었다.

먼저 테스트 코드가 필요한 Controller, Service, Repository에 대해 테스트 작성 흐름을 알게 되었다.
이 부분이 솔직히 정말 의미있다고 생각하며, 앞으로 어렵게만 느껴졌던 테스트 코드 작성에 대한 부담감을 덜 수 있을것 같다.

두번째로는 여러 분기에 대한 처리의 중요성을 알게 되었다.
테스트 커버리지 100%를 목표로 채워나가다 보니 사용한 적 없는 조건문이나 고려해야하는 예외 상황들을 많이 보게 되었다.
이런 부분에 대한 작성을 실제로 하다 보니 예상치 못한 부분에서 발생할 수 있는 상황을 좀 더 생각해 볼 수 있었고, 불필요한 코드들을 처리할 수 있었던 것 같다.

마지막으로 테스트 코드의 중요성을 다시 한 번 깨닫게 되었다.
테스트 코드를 작성해보면서 실제로 잘못 구현한 부분도 발견했으며, 위에서 언급했던 것 처럼 불필요한 부분이나 어색한 부분들을 고칠 수 있었다.
이러한 자잘한 실수들이 실무에서는 큰 영향을 미칠 수 있으니 테스트 코드가 얼마나 중요한 지 알 수 있었던 것 같다.

마치며

일주일이 넘는 시간동안 테스트 코드를 작성해보면서 여러 생각들이 들었다.
작은 프로젝트임에도 불구하고 작성해야 할 테스트는 많았고 관련 지식도 부족했기에 생각보다 힘들었고 시간이 꽤나 소요되었다.
그래도 매일 새벽까지 테스트를 작성하며 테스트 커버리지를 올려나가는 순간들은 재밌었고 무엇보다 의미있는 작업이라는 것을 많이 느끼게 되었다.

코치님께서 추천해주셨던 영상에서도 나왔던 것 처럼 테스트 커버리지 100%를 달성한 순간에 "아 이렇게 해도 모든 상황을 검증할 수는 없겠구나" 라는 생각이 들었고 그렇기에 테스트 커버리지 100% 달성은 좋은 경험이었고 앞으로도 최대한 버그가 없도록 좋은 테스트 코드를 열심히 작성하며 개발을 해나가야겠다고 다짐하게되었다.

테스트 결과

간단한 UI 추가하기

커밋 내용 =>

11/12 화 feat: 간단한 UI 추가하기 - 부트스트랩 템플릿
11/13 수 feat: 간단한 UI 추가하기 - Fragment 분리, 기본 구조 설정
test: 간단한 UI 추가하기 - MusicListViewController 테스트 작성
11/14 목 feat: 간단한 UI 추가하기 - 템플릿 수정 (MusicList)
11/15 금 feat: 간단한 UI 추가하기 - 로그인 페이지 추가
fix: 간단한 UI 추가하기 - card 내 아이콘 svg로 수정
11/16 토 feat: 간단한 UI 추가하기 - 음악/플레이리스트 페이지 추가
11/17 일 test: 간단한 UI 추가하기 - MusicListViewController, MemberViewControllerTest

메인화면

Main

로그인화면

Login

재생목록 생성 화면

Playlist

후기

크게 어려운 작업은 아니었지만 기존 기능에 대한 화면만 구현할지 새 기능들을 추가해서 구현할지에 대해서 고민이 조금 되었다.
결과적으로는 구현하기로 마음을 먹었으며 UI 자체는 더 추가할 부분이 없을 것 같아 일단락하려고 한다.

향후 추가할 기능은 아래와 같다.
- 음악 검색 (Kotlin JDSL)
- Github 로그인
- 재생목록 템플릿 제공

새로운 프로젝트를 진행하게 되어서 당장은 힘들겠지만 차근차근 해나가보려고한다.

더미 데이터 추가하기

커밋 내용 =>

11/30 토 feat: 더미 데이터 추가하기
Swagger 적용해보기

커밋 내용 =>

11/30 토 feat: Swagger 추가 및 설정

Swagger UI

/swagger-ui/index.html

SwaggerUI

📑 참고

본 프로젝트는 인프런 워밍업 클럽 스터디 2기 - 백엔드 프로젝트 (Kotlin, Spring Boot) 의 서브 미션을 위한 프로젝트입니다.

🎯 미션 수행 사항
  • [미션 1] 테이블 설계하기(~10/4 금)
  • [미션 2] 깃허브 리포지토리에 프로젝트 올리기(~10/4 금)
  • [미션 3] REST API 설계하기(~10/8 화)
  • [미션 4] 조회 REST API 만들기(~10/15 화)
  • [미션 5] 삽입, 수정, 삭제 REST API 만들기(~10/21 월)
  • [자체 미션] 이후 고도화 (~ 01/12 일)
📑 고도화 계획

코드 리뷰 => 코드 리뷰 코드 리뷰 해주신 내용을 고려하여 코드를 수정하고, 고도화를 진행해보았습니다.

  • Restful 하도록 API 수정하기 (10/30 수)
  • 읽기 좋은 코드로 수정하기 (주석, 메서드 네이밍 수정, 퍼사드 패턴 적용) (10/31 목 ~ 11/01 금)
  • 테스트 커버리지 100% 달성하기 (단위 테스트 위주로 작성) (11/02 토 ~ 11/11 월)
  • 간단한 UI 추가하기 (Thymeleaf) (11/12 화 ~ 11/17 일)
  • 더미 데이터 추가하기 (11/30 토)
  • Swagger 적용 (11/30 토)

추가 기능 구현 계획

  • 음악 검색 (Kotlin JDSL) (11/30 토)
  • 재생목록 템플릿 제공 (01/12 일)

About

Add your own music list - My Music List

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors