[SPRING] Todo project | JDBC, DB 연결(SQL), Layered Architecture, JdbcTemplate

림민지·2025년 3월 24일

Today I Learn

목록 보기
31/62

Todo 리스트를 생성, 조회, 수정, 삭제할 수 있는 프로그램을 만들어보자.

  1. JDBC 사용
  2. DB를 연결해 지속적으로 데이터를 관리
  3. MVC 패턴에 추가적으로 Service Layer 와 Repository Layer 를 분리하여 유지보수와 확장성을 강화

👾 전처리

1. 요구사항 정리

0. API문서 작성

1. 일정 생성

  • 할일, 작성자명, 비밀번호, 작성/수정일을 저장
  • 작성/수정일은 날짜와 시간을 모두 포함한 형태
  • 각 일정의 고유 식별자(ID)를 자동으로 생성하여 관리
  • 최초 입력 시, 수정일은 작성일과 동일

2. 전체 일정

3. 선택 일정 조회

  • 선택한 일정 단건의 정보를 조회할 수 있습니다.
  • 일정의 고유 식별자(ID)를 사용하여 조회합니다.

4. 선택 일정 수정

  • 선택한 일정 내용 중 할일, 작성자명 만 수정 가능
  • 서버에 일정 수정을 요청할 때 비밀번호를 함께 전달합니다.
  • 작성일 은 변경할 수 없으며, 수정일 은 수정 완료 시, 수정한 시점으로 변경합니다.\

5. 선택 일정 삭제

  • 선택한 일정을 삭제할 수 있습니다.
  • 서버에 일정 수정을 요청할 때 비밀번호를 함께 전달합니다.

2. API 문서 작성

노션을 활용해서 작성했다.
각 기능별로 HttpMethod, URL, 요청&반환, 상태코드, 설명을 담았다.


🎮 패키지 분리 & 데이터 관리

1. 계층형 아키텍처(Layered Architecture)

각각이 맡고있는 기능을 확실하게 정리하고, 가독성을 높이기 위해
MVC 패턴을 기반으로 한 계층형 아키텍처(Layered Architecture) 패턴을 활용했다!

이렇게 하면 MVC 패턴에 추가적으로 Service Layer 와 Repository Layer 를 분리했기 때문에 유지보수와 확장성을 강화한 구조가 된다!

구조 정리
Controller : 사용자의 요청 받고 적절한 서비스 메서드 호출, 결과 응답으로 반환
→ @GetMapping, @PostMapping 등
Service : 컨트롤러에서 받은 요청을 처리
Repository : 데이터베이스와 상호작용하여 데이터 저장 & 조회
Entity : 데이터베이스의 테이블 구조를 정의
DTO : 클라이언트와 서버 사이에서 데이터를 주고받을 때

2. 데이터 관리

[ SQL 정의하기 ]

투두의 객체들을 정의하고 테이블을 만들어 관리하자
먼저 Spring DB에 todo 테이블을 만들었다.

id는 생성됨에 따라 자동으로 증가 - AUTO_INCREMENT
생성일과 업데이트 날짜가 자동으로 반영되어야하므로 CURRENT_TIMESTAMP

[ JdbcTemplate 이용하기 ]

아까 만들었던 repository 패키지의 TodoRepository에 정의해주고 사용했다.

private final JdbcTemplate jdbcTemplate;

public JdbcTemplateTodoRepository(JdbcTemplate jdbcTemplate) {
	this.jdbcTemplate = jdbcTemplate;
    }

🔥 기능 구현

1️⃣ 데이터 저장

1. Todo entity

@Getter
@AllArgsConstructor
public class Todo {
    private String userName; //일정 등록한 사람 이름
    private String password; //비번
    private String todo; //할일 내용
    @JsonFormat(pattern = "YYYY-MM-DD")
    private Date doDate; //예정 날짜

    private LocalDateTime createDate;
    private LocalDateTime updateDate;
}

아까 DB에 정의했던 것처럼 객체들을 private로 정의해주고
@Getter @AllArgsConstructor로 Getter와 생성자를 자동 생성!

2. repository

먼저 interface로 구조를 정의해주고

public interface TodoRepository {
    void saveTodo(Todo todo);
   }

이후에 Override해서 내부를 정의했다

@Repository
public class JdbcTemplateTodoRepository implements TodoRepository{
    private final JdbcTemplate jdbcTemplate;

    public JdbcTemplateTodoRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void saveTodo(Todo todo) {
        String sql = "INSERT INTO todo (userName, password, todo, doDate, createDate, updateDate) VALUES (?,?,?,?,?,?) "; 
        jdbcTemplate.update(sql, todo.getUserName(), todo.getPassword(),
        todo.getTodo(),todo.getDoDate(), todo.getCreateDate(), todo.getUpdateDate());
    }

Todo의 getter를 사용해서 sql문을 만들고 jdbcTemplate를 활용해 update해주자!

3. DTO

[ RequestDto ]

@Getter
public class RequestDto {
    private Long id;
    private String userName; //일정 등록한 사람 이름
    private String password; //비번
    private String todo; //할일 내용
    private Date doDate; //예정 날짜
    private LocalDateTime createDate;
    private LocalDateTime updateDate;

}

요청할 값들을 정의!

[ ResponseDto ]

@Getter
@AllArgsConstructor
public class ResponseDto {
    private String userName; //일정 등록한 사람 이름
    private String todo; //할일 내용
    private Date doDate; //예정 날짜
    private LocalDateTime createDate;
    private LocalDateTime updateDate;
}

사용자에게 응답할(ResponseDto)를 정의한다.
사용자에게 노출할때는 비밀번호가 보이면 안되므로 pw를 제외하고 만든다.

4. service

servie 패키지에 먼저 TodoService interface를 정의하고

public interface TodoService {
    ResponseDto saveTodo(RequestDto dto);
}

TodoServiceImpl 클래스 파일에 implement해주자

@Service
public class ToDoServiceImpl implements TodoService{

    private final TodoRepository todoRepository;

    public ToDoServiceImpl(TodoRepository todoRepository) {
        this.todoRepository = todoRepository;
    }

    @Override
    public ResponseDto saveTodo(RequestDto dto) { 
      Todo todo = new Todo(dto.getUserName(), dto.getPassword(), dto.getTodo(), dto.getDoDate(), LocalDateTime.now(), LocalDateTime.now()); //투두 내역 받아서
        todoRepository.saveTodo(todo); //레포지토리에 넘겨주자
        return new ResponseDto(todo.getUserName(), dto.getTodo(), dto.getDoDate(), LocalDateTime.now(), LocalDateTime.now());
    }

입력받은 todo의 내용들을 레포지토리에 전해주고,
response에도 저장스~

5. controller

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

    private final TodoService todoService;

    public TodoController(TodoService todoService) { //생성자
        this.todoService = todoService;
    }
    @PostMapping //투두 추가하는거니까 Post
    public ResponseDto saveTodo(@RequestBody RequestDto dto){ //추가하라고 요청하기
        return todoService.saveTodo(dto);
    }

prefix url로 "/todos" 설정해주고

아까 만든 service를 가져온 후, savdTodo를 사용해주자
@RequestBody를 사용하면 요청 보내기 쌉가능이고 추가기능이니까 @PsotMapping 어노테이션을 사용하자

이렇게하면 포스트맨에서 잘 추가되는 모습을 확인할 수 있다!


2️⃣ 데이터 전체 조회

repository

마찬가지로 interface를 구현한 후 Override해준다
sql문을 작성하는데, 이때 비밀번호는 노출되면 안되므로 *이 아니라 직접 지정해준다

@Override
public List<ResponseDto> findAllTodo() {
	String sql = "SELECT userName, todo, doDate, createDate, updateDate FROM todo";
    return jdbcTemplate.query(sql, (rs, rowNum) ->
    	new ResponseDto(
        	rs.getString("userName"),
            rs.getString("todo"),
            rs.getDate("doDate"),
            rs.getTimestamp("createDate").toLocalDateTime(),
            rs.getTimestamp("updateDate").toLocalDateTime()
                )
        );
    }

그리고 JdbcTemplatequery 를 사용해서 행별로 출력할 수 있도록 해주자(for문과 비슷하다~!)

service

service에서는 리스트로 받을 수 있도록 List<> 형태로 만들어주고 아까 레포에서 받은 것을 리턴해준다

    @Override
    public List<ResponseDto> findAllTodo() {
        List<ResponseDto> allTodo = todoRepository.findAllTodo();
        return allTodo;
    }

controller

마지막으로 컨트롤러에서 @GetMapping을 사용해 전체 일정을 가져와주면 완성!!

    @GetMapping //전체 일정 가져오기
    public List<ResponseDto> findAllTodo(){
        return todoService.findAllTodo();
    }

3️⃣ 선택 일정 조회

마찬가지로 레포에서 데이터 찾기 - 서비스에서 받아오기 - 컨트롤러가 실행할 수 있게 연결해주기 !!

repository

    @Override
    public ResponseDto findOneTodo(Long id){
        String sql = "SELECT * FROM todo WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, (rs, rowNum) ->
                new ResponseDto(
                        rs.getString("userName"),
                        rs.getString("todo"),
                        rs.getDate("doDate"),
                        rs.getTimestamp("createDate").toLocalDateTime(),
                        rs.getTimestamp("updateDate").toLocalDateTime()
                ), id
        );
    }

id를 활용해서 찾아야하기 때문에 id를 인자로 넣어주자
여기서 위에 썼던 query queryForObject의 다른점은 다음과 같다

queryqueryForObject()
여러 개의 결과(목록)를 조회할 때 사용단일 결과(한 개의 행)를 조회할 때 사용
여러 개의 행을 리스트로 변환결과가 반드시 하나여야 함

service

넘겨받자

    @Override
    public ResponseDto findTodoById(Long id) {
        return todoRepository.findOneTodo(id);
    }

controller

마지막으로 사용자가 url을 통해 접근 가능하도록 GetMapping해주자
@PathVariable 어노테이션으로 알맞은 id 매칭하기

    @GetMapping("/{id}") //선택 일정 가져오기
    public ResponseDto findTodoById(@PathVariable Long id){
        return todoService.findTodoById(id);
    }

지금까지 데이터를 저장하고 조회하는 기능을 구현했다. 다음에는 수정하고 삭제해보자!

profile
@lim_128

0개의 댓글