20231228 (목) TodoApp 완성

2023. 12. 28. 20:22TIL

오늘 낮에 TodoApp을 완성했고, 남은 시간엔 코드작성이 어려웠던 부분을 되새김질 하며 공부하는데 시간을 보냈다.

 

domain을 todocard, todo, comment로 나누었지만 

그중 가장 중요한 todocard 패키지만 일단 나열해보겠다.

 

<TodoCard>

 

TodoCard조회시 뒤에 적을 Todo와 Comment를 포함할 예정

또 TodoCard 패키지 하위에는 dto, controller, service, model, repository패키지를 포함하고있다.

 

dto 패키지

 

CreateTodoCardRequest

package com.example.mytodoapp.domain.todocard.dto

data class CreateTodoCardRequest(
        val user: String,
        val password: String,
)

TodoCardResponse

package com.example.mytodoapp.domain.todocard.dto

data class TodoCardResponse(
        val id: Long,
        val user: String,
        val date: String,
)

UpdateTodoCardRequest

package com.example.mytodoapp.domain.todocard.dto

data class UpdateTodoCardRequest(
        val user: String,
)

 

dto는 프로세스 간에 데이터를 전달하는 개체이다.

전체적으로 TodoCard는 id, user, password, date 등을 포함하고 있지만 상황에따라서 필요한 데이터가 다르기에 이런식으로 나눠두는 것.

 

controller 패키지

 

TodoCardController

package com.example.mytodoapp.domain.todocard.controller

import com.example.mytodoapp.domain.todocard.dto.CreateTodoCardRequest
import com.example.mytodoapp.domain.todocard.dto.TodoCardResponse
import com.example.mytodoapp.domain.todocard.dto.UpdateTodoCardRequest
import com.example.mytodoapp.domain.todocard.service.TodoCardService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RequestMapping("/todoCards")
@RestController
class TodoCardController(
        private val todoCardService: TodoCardService
) {

    @PostMapping
    fun createTodoCard(@RequestBody createTodoCardRequest: CreateTodoCardRequest)
    : ResponseEntity<TodoCardResponse>{
        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(todoCardService.createTodoCard(createTodoCardRequest))
    }

    @GetMapping()
    fun getTodoCardList(): ResponseEntity<List<TodoCardResponse>>{
        return ResponseEntity
                .status(HttpStatus.OK)
                .body(todoCardService.getAllTodoCardList())
    }

    @GetMapping("/{todoCardId}")
    fun getTodoCard(@PathVariable todoCardId: Long): ResponseEntity<TodoCardResponse> {
        return ResponseEntity
                .status(HttpStatus.OK)
                .body(todoCardService.getTodoCardById(todoCardId))
    }

    @PutMapping("/{todoCardId}")
    fun updateTodoCard(
            @PathVariable todoCardId: Long,
            @RequestBody updateTodoCardRequest: UpdateTodoCardRequest
    ): ResponseEntity<TodoCardResponse> {
        return ResponseEntity
                .status(HttpStatus.OK)
                .body(todoCardService.updateTodoCard(todoCardId, updateTodoCardRequest))
    }

    @DeleteMapping("/{todoCardId}/{password}")
    fun deleteTodoCard(
            @PathVariable todoCardId: Long,
            @PathVariable password: String,
            ): ResponseEntity<Unit>{
        return ResponseEntity
                .status(HttpStatus.NO_CONTENT)
                .body(todoCardService.deleteTodoCard(todoCardId, password))
    }
}

 

@RestController는 스프링 빈으로 등록하기 위해 작성된 어노테이션

또 각각의 매핑을 붙여주면서 메소드의 역할을 지정해주는 듯 싶다.

매핑옆에 오는 /todocard/{todoCardId}/ 등 이런거는 직접 실행된 화면을 보니까 페이지 주소쪽을 나타내는 것인듯 하다.

 

repository 패키지

TodoCardRepository

package com.example.mytodoapp.domain.todocard.repository

import com.example.mytodoapp.domain.todocard.model.TodoCard
import org.springframework.data.jpa.repository.JpaRepository

interface TodoCardRepository: JpaRepository<TodoCard, Long> {
}

 

단순하게 JpaRepository를 상속받으면 스프링에서 알아서 레포지토리 파일인것을 인지해준다고 한다.

이런 편리한 면이 스프링을 쓰는 이유인가보다.

 

model 패키지

TodoCard

package com.example.mytodoapp.domain.todocard.model

import com.example.mytodoapp.domain.comment.model.Comment
import com.example.mytodoapp.domain.todo.model.Todo
import com.example.mytodoapp.domain.todocard.dto.TodoCardResponse
import jakarta.persistence.*
import java.time.LocalDateTime

var now = LocalDateTime.now()

@Entity
@Table(name = "todocard")
class TodoCard (

        @Column(name = "app_user", nullable = false)
        var app_user: String,

        @Column(name = "password", nullable = false)
        var password: String,

        @Column(name = "date", nullable = false)
        val date: String = "${now.year}-${now.month}-${now.dayOfMonth}",

        @OneToMany(
                cascade = [CascadeType.ALL],
                orphanRemoval=true,
                fetch = FetchType.LAZY)
        @JoinColumn(name = "todocard_id")
        var todoList: MutableList<Todo> = mutableListOf(),

        @OneToMany(
                cascade = [CascadeType.ALL],
                orphanRemoval=true,
                fetch = FetchType.LAZY)
        var comments: MutableList<Comment> = mutableListOf()



) {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Long? = null

        fun addComment(comment: Comment) {
                comments.add(comment)
        }

        fun removeComment(comment: Comment) {
                comments.remove(comment)
        }

        fun addTodo(todo: Todo) {
                todoList.add(todo)
        }

        fun removeTodo(todo: Todo) {
                todoList.remove(todo)
        }
}

fun TodoCard.toResponse(): TodoCardResponse {
        return TodoCardResponse(
                id = id!!,
                user = app_user,
                date = date
        )
}

 

내가 만든 데이터베이스의 테이블과 DTO를 연결해주는 파일이다.

테이블 하나하나의 컬럼과 DTO의 변수들을 지정해주고,

Id의 경우 @GeneratedValue(strategy = GenerationType.IDENTITY) 를 써서 자동입력되도록 했다.

 

service 패키지

 

앱에서 사용될 메소드를 정리해둔 패키지인데

내가 이해한대로 적자면

클라이언트들은 아래 파일을 접하게 될거고

TodoCardService

package com.example.mytodoapp.domain.todocard.service

import com.example.mytodoapp.domain.todocard.dto.CreateTodoCardRequest
import com.example.mytodoapp.domain.todocard.dto.TodoCardResponse
import com.example.mytodoapp.domain.todocard.dto.UpdateTodoCardRequest

interface TodoCardService {
    fun getAllTodoCardList(): List<TodoCardResponse>

    fun getTodoCardById(todoCardId: Long): TodoCardResponse

    fun createTodoCard(request: CreateTodoCardRequest): TodoCardResponse

    fun updateTodoCard(todoCardId: Long, request: UpdateTodoCardRequest): TodoCardResponse

    fun deleteTodoCard(todoCardId: Long, password: String,)
}

 

 

메소드들이 상세하게 적혀있는 파일은 위 파일을 상속받은

TodoCardServiceImpl 

package com.example.mytodoapp.domain.todocard.service

import com.example.mytodoapp.domain.exception.IncorrectPasswordException
import com.example.mytodoapp.domain.exception.ModelNotFoundException
import com.example.mytodoapp.domain.todocard.dto.CreateTodoCardRequest
import com.example.mytodoapp.domain.todocard.dto.TodoCardResponse
import com.example.mytodoapp.domain.todocard.dto.UpdateTodoCardRequest
import com.example.mytodoapp.domain.todocard.model.TodoCard
import com.example.mytodoapp.domain.todocard.model.toResponse
import com.example.mytodoapp.domain.todocard.repository.TodoCardRepository
import jakarta.transaction.Transactional
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service

@Service
class TodoCardServiceImpl(
        private val todoCardRepository: TodoCardRepository
): TodoCardService {
    override fun getAllTodoCardList(): List<TodoCardResponse> {
        return todoCardRepository.findAll().map{ it.toResponse() }
    }

    override fun getTodoCardById(todoCardId: Long): TodoCardResponse {
        val todoCard = todoCardRepository.findByIdOrNull(todoCardId)
                ?: throw ModelNotFoundException("TodoCard", todoCardId)
        return todoCard.toResponse()
    }

    @Transactional
    override fun createTodoCard(request: CreateTodoCardRequest): TodoCardResponse {
        return todoCardRepository.save(
                TodoCard(
                        app_user = request.user,
                        password = request.password
                )
        ).toResponse()
    }

    @Transactional
    override fun updateTodoCard(todoCardId: Long, request: UpdateTodoCardRequest): TodoCardResponse {
        val todoCard = todoCardRepository.findByIdOrNull(todoCardId)
                ?: throw ModelNotFoundException("TodoCard", todoCardId)
        val (user) = request

        todoCard.app_user = user

        return todoCardRepository.save(todoCard).toResponse()
    }

    @Transactional
    override fun deleteTodoCard(todoCardId: Long, password: String) {
        val todoCard = todoCardRepository.findByIdOrNull(todoCardId)
                ?: throw ModelNotFoundException("TodoCard", todoCardId)
        if (password == "masterPW5946" || password == todoCard.password ){
            todoCardRepository.delete(todoCard)
        } else {
            throw IncorrectPasswordException(password, todoCardId)
        }

    }
}

이것이다.

 

오늘의 한마디 : 일단 완성은 했지만 출제의도 대로 작성한것인지는 모르겠다. 빨리 피드백을 받아보고싶다.