본문 바로가기

카테고리 없음

Todo 정리

할일, 즉 Todo를 만들어야 한다.

 

  • [ ] 생성 - 일정 작성
    • [ ] 할 일 제목, 할 일 내용, 담당자, 비밀번호, 작성일 을 저장할 수 있습니다.
      • [ ] 저장된 일정 정보를 반환 받아 확인할 수 있습니다.
  • [ ] 조회 - 선택한 일정 조회(상세 페이지)
    • [ ] 선택한 일정의 정보를 조회할 수 있습니다.
    • [ ] 반환 받은 일정 정보에는 할 일 제목,할 일 내용, 작성일, 작성자 이름 정보가 들어있습니다.
  • [ ] 조회 - 일정 목록 조회
    • [ ] 등록된 일정 전체를 조회할 수 있습니다.
    • [ ] 조회된 일정 목록은 작성일 기준 내림차순으로 정렬 되어있습니다.
  • [ ] 수정 - 선택한 일정 수정
    • [ ] 선택한 일정의 할 일 제목, 할 일 내용, 담당자를 수정할 수 있습니다.
    • [ ] 서버에 일정 수정을 요청할 때 비밀번호를 함께 전달합니다.
      • [ ] 생성 시에, 입력한 비밀번호와 일치할 경우에만 수정할 수 있습니다.
    • [ ] 수정된 일정의 정보를 반환 받아 확인할 수 있습니다.
  • [ ] 삭제 - 선택한 일정 삭제
    • [ ] 서버에 일정 삭제를 요청할 때 비밀번호를 함께 전달합니다.
      • [ ] 생성 시에, 입력한 비밀번호와 일치할 경우에만 수정할 수 있습니다.

 

이걸 작업하기 위해 먼저,

//JPA사용을 위해 추가
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    //데이터베이스 추가. 이거 안하면 안됨
    implementation("com.h2database:h2")

이걸 추가해야만 jpa를 사용할수있고 데이터베이스가 없으면 정보의 저장이 안되기 때문인지 h2설정도 하기로 했다.

 

 

정확히는

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class

Action:

Consider the following: If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

 

이런 오류가 덧는데 이걸봐도 역시 h2를 설정해야한다는걸 알수 있었다.

그리고

 

[ ] 할 일 제목, 할 일 내용, 담당자, 비밀번호, 작성일 을 저장할 수 있습니다.이걸 위해서

 

package com.teamsparta.model;
//lombok를 통해 Getter,Setter,기본 생성자를 자동 생성
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
@NoArgsConstructor
public class Todo {

    private String title;// 제목
    private String content;//내용
    private String assignee;//담당자
    private String password;//비번
    private Date creationDate;//작성일

}

 

이렇게 요구사항을 충족한 엔티티를 만들어준다. 중간에 lombok란걸 깔았기 때문에 getter, setter등을 전부 @Getter
이런걸 넣음으로서 자동으로 설정해준다고 한다.

 

그리고 서비스랑 서비스 임플을 만든다.

package com.teamsparta.service;

import com.teamsparta.model.Todo;

import java.util.List;

public interface TodoService {
    Todo create(Todo todo);
    Todo get(Long id);
    List<Todo> getAll();
    Todo update(Long id, Todo todo);
    void delete(Long id);
}

 

서비스는 이렇게 생성, 조회, 전체조회, 수정, 삭제를 만들었다가, dto를 써야한다는걸 떠올려서 바꾸기로 했다.

먼저,

 

@Setter
@Getter
public class TodoRequestDTO {
    private String title;
    private String content;
    private String assignee;
    private String password;
}


@Setter
@Getter
public class TodoResponseDTO {
    private String title;
    private String content;
    private String assignee;
    private Date creationDate;
}

 

 

이렇게 생성에 필요한 리퀘스트와 반환된 화면에 보여줄 리스폰스용 dto를 만든다.

이것들엔 getter, setter을 넣었기 때문에 이것과 연결될 서비스 등에서 거더랑 세터를 이용할수 있게될것이다.

 

그리고 서비스를 만드는데, dto와 연결하고 

 

      • 생성 시에, 입력한 비밀번호와 일치할 경우에만 수정할 수 있습니다.
    • [ ] 수정된 일정의 정보를 반환 받아 확인할 수 있습니다.
  • [ ] 삭제 - 선택한 일정 삭제
    • [ ] 서버에 일정 삭제를 요청할 때 비밀번호를 함께 전달합니다.
      • [ ] 생성 시에, 입력한 비밀번호와 일치할 경우에만 수정할 수 있습니다.

이걸 이루기 위해

 

public interface TodoService {
    //todo로 했던것들을 TodoResponseDTO이렇게 dto랑 연결
    TodoResponseDTO create(TodoRequestDTO todoRequestDTO);//TodoRequestDTO이거랑 연결하려면 TodoRequestDTO todoRequestDTO 해야함
    // TodoRequestDTO와 연결되어 생성후 TodoResponseDTO를 반환
    TodoResponseDTO get(Long id);// 누르면 TodoResponseDTO를 반환
    List<TodoResponseDTO> getAll();//누르면 TodoResponseDTO를 반환하지만, list, 즉 목록을 반환
    TodoResponseDTO update(Long id, TodoRequestDTO todoRequestDTO, String password);
    void delete(Long id, String password);// dto와 연결되지도, 뭔가를 반환하지도 않으니 void
}

 

 

이렇게 TodoRequestDTO todoRequestDTO로 dto와연결되 작성하고 TodoResponseDTO를 반환하는걸 작성하고

수정, 삭제에는 비밀번호를 인식하게 string password를 넣어준다.

 

package com.teamsparta.service;

import com.teamsparta.dto.TodoRequestDTO;
import com.teamsparta.dto.TodoResponseDTO;
import com.teamsparta.model.Todo;
import com.teamsparta.repository.TodoRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.stream.Collectors;


import java.util.Date;
import java.util.List;

@Service
@RequiredArgsConstructor
public class TodoServiceImpl implements TodoService {

    private final TodoRepository todoRepository;

    @Override
    public TodoResponseDTO  create(TodoRequestDTO todoRequestDTO) {//리퀘스트를 받아 스시폰스를 반환
        Todo todo = new Todo();//새로운 todo를 생성
        todo.setTitle(todoRequestDTO.getTitle());//setter과 getter로 todoRequestDTO에서 제목 정보를 가져와 todo 객체의 title에 설정
        todo.setContent(todoRequestDTO.getContent());//마찬가지의 방법으로 내용을 가져옴
        todo.setAssignee(todoRequestDTO.getAssignee());
        todo.setPassword(todoRequestDTO.getPassword());
        todo.setCreatedAt(new Date());//현재 날짜를 저장

        Todo createdTodo = todoRepository.save(todo);//todoRepository를 이용해 todo 객체를 데이터베이스에 저장하고, 저장된 todo를 createdTodo에 저장
        return convertToResponseDTO(createdTodo); // Todo를 TodoResponseDTO로 변환
//        return todoRepository.save(todo);
    }

//    @Override dto 연결전
//    public Todo get(Long id) {
//        return todoRepository.findById(id)
//                .orElseThrow(() -> new RuntimeException("Todo not found"));
//    }
    @Override
    public TodoResponseDTO get(Long id) {
        Todo todo = todoRepository.findById(id)//todo의 아이디로 조회
                .orElseThrow(() -> new RuntimeException("Todo not found"));//Optional인 orElseThrow로 아이디가 없으면 익셉션
        return convertToResponseDTO(todo); // Todo를 TodoResponseDTO로 변환,
    }

//    @Override
//    public List<Todo> getAll() {
//        return todoRepository.findAllByOrderByIdDesc();
//    }
    @Override
    public List<TodoResponseDTO> getAll() {
        List<Todo> todos = todoRepository.findAllByOrderByCreatedAtDesc();//내림차순으로 모든 todo를 가져옴 밑에 에외처리

        if (todos.isEmpty()) {
            throw new RuntimeException("No todos found");
        }//데이터 비엇을경우 예외처리

        List<TodoResponseDTO> todoResponseDTOs = todos.stream()
                .map(this::convertToResponseDTO)
                .collect(Collectors.toList());
        return todoResponseDTOs; // List<Todo>를 List<TodoResponseDTO>로 변환
    }//아무것도 없을때 예외처리 추가하기, 이거 좀더 살펴보기

    @Override
    public TodoResponseDTO update(Long id, TodoRequestDTO todoRequestDTO, String password) {
//비번, 아이디, 리퀘스트를 받아 리스폰스를 반환
//    public Todo update(Long id, TodoRequestDTO todoRequestDTO, String password) {
        Todo existingTodo = todoRepository.findById(id)//아이디 찾고 없으면 예외
                .orElseThrow(() -> new RuntimeException("Todo not found"));

        if (!existingTodo.getPassword().equals(password)) {//비번 안맞으면 예외
            throw new RuntimeException("Invalid password");
        }
//dto의 getter, setter 덕분에 사용
        existingTodo.setTitle(todoRequestDTO.getTitle());//todoRequestDTO의 getTitle()를 호출하여 할 일의 제목을 가져옴
        existingTodo.setContent(todoRequestDTO.getContent());
        existingTodo.setAssignee(todoRequestDTO.getAssignee());

        existingTodo.setPassword(todoRequestDTO.getPassword());// 비밀번호도 수정 되니까 추가


        Todo updatedTodo = todoRepository.save(existingTodo);
        return convertToResponseDTO(updatedTodo); // Todo를 TodoResponseDTO로 변환
        //        return todoRepository.save(existingTodo);

    }

    @Override
    public void delete(Long id, String password) {
        Todo todo = todoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Todo not found"));

        if (!todo.getPassword().equals(password)) {
            throw new RuntimeException("Invalid password");
        }

        todoRepository.delete(todo);
    }

    private TodoResponseDTO convertToResponseDTO(Todo todo) {//todo를 TodoResponseDTO로 전환
        TodoResponseDTO responseDTO = new TodoResponseDTO();//새로운 TodoResponseDTO 생성
        // dto의 setter 덕분에 사용
        responseDTO.setTitle(todo.getTitle());//todo의 제목을 TodoResponseDTO로 가져옴
        responseDTO.setContent(todo.getContent());
        responseDTO.setAssignee(todo.getAssignee());
        responseDTO.setCreationDate(todo.getCreatedAt());
        return responseDTO;
    }
}

 

자바에서 dto를 제대로 쓰려면, 

    private TodoResponseDTO convertToResponseDTO(Todo todo) {//todo를 TodoResponseDTO로 전환
        TodoResponseDTO responseDTO = new TodoResponseDTO();//새로운 TodoResponseDTO 생성
        // dto의 setter 덕분에 사용
        responseDTO.setTitle(todo.getTitle());//todo의 제목을 TodoResponseDTO로 가져옴
        responseDTO.setContent(todo.getContent());
        responseDTO.setAssignee(todo.getAssignee());
        responseDTO.setCreationDate(todo.getCreatedAt());
        return responseDTO;

 

이렇게 컨버트를 넣는것도 나쁘지 않은거 같다. 물론 없이해도 문제는 없다는 모양이지만, 코틀린에는 이런걸 해본적이 없기 때문에 빼보았다. 이건  생성된todo를 리스폰스 dto로 변환시켜서 우리가 볼수있도록 해준다.

그래서 리스폰스에 있는 제목, 내용등등을 포함하고 있는데, 또 id를 빼먹었길래 추가했다...

responseDTO.setId(todo.getId());

이렇게 하고, 또 responsedto도 아이디를 빼먹었길래 추가해 주었다.

 

 

 

내림차순으로 정렬하기 위해서

@Repository
public interface TodoRepository extends JpaRepository<Todo, Long> {
    List<Todo> findAllByOrderByIdDesc();
}

 

이걸 추가했는데, 이게 바로 모든 todo를 찾아서 아이디를 내림차순 id가 없다고 뜨는게 아닌가?

그건 바로 todo에 아이디를 넣는검 깜빡했던 것이다. 그래서 넣었는데... 생각해보니 아이디순이 아니라 날짜순이라는걸 깨달았다.

 

그래서

@Getter
@Setter
@NoArgsConstructor
public class Todo {

    private Long id; // 고유 id
    private String title;// 제목
    private String content;//내용
    private String assignee;//담당자
    private String password;//비번
    private Date createdAt;//작성일
}

 

이렇게 아이디도 넣고 날짜도 친숙한 createdAt을 쓰는게 나을거 같아서 변경,

리포지터리도

@Repository
public interface TodoRepository extends JpaRepository<Todo, Long> {
    List<Todo> findAllByOrderByCreatedAtDesc();
}

 

이렇게 내림차순으로 정렬하도록 바꿔준다.

 

그리고 마지막으로

 

package com.teamsparta.controller;

import com.teamsparta.dto.TodoRequestDTO;
import com.teamsparta.dto.TodoResponseDTO;
import com.teamsparta.service.TodoService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/todos")
@RequiredArgsConstructor
public class TodoController {

    private final TodoService todoService;

    @PostMapping
    public ResponseEntity<TodoResponseDTO> createTodo(@RequestBody TodoRequestDTO todoRequestDTO )
    {//TodoRequestDTO를 입력받아 todo 생성
        TodoResponseDTO createdTodo = todoService.create(todoRequestDTO);//서비스를 통해 리퀘스트로 생성, 리스폰스 반환
        return ResponseEntity.status(HttpStatus.CREATED).body(createdTodo);//완료하면 201 반환
    }

    @GetMapping("/{id}")
    public ResponseEntity<TodoResponseDTO> getTodo(@PathVariable Long id) {
        TodoResponseDTO todoResponseDTO = todoService.get(id);
        return ResponseEntity.ok(todoResponseDTO);//완료시 리스폰스 보여줌
    }


    @GetMapping
    public ResponseEntity<List<TodoResponseDTO>> getAllTodos() {
        List<TodoResponseDTO> todoResponseDTOs = todoService.getAll();
        return ResponseEntity.ok(todoResponseDTOs);
    }

    @PutMapping("/{id}")
    public ResponseEntity<TodoResponseDTO> updateTodo(@PathVariable Long id, @RequestBody TodoRequestDTO todoRequestDTO, @RequestParam String password) {
        TodoResponseDTO updatedTodoResponseDTO = todoService.update(id, todoRequestDTO, password);
        return ResponseEntity.ok(updatedTodoResponseDTO);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteTodo(@PathVariable Long id, @RequestParam String password) {
        todoService.delete(id, password);
        return ResponseEntity.noContent().build();
    }
}