CRUD - Read One, Update, Delete

calis_ws·2023년 6월 11일
0
post-custom-banner

Controller와 Service 분리

StudentDto.class

public class StudentDto {
    private Long id;
    private String name;
    private String email;

    public StudentDto() {
    }

    public StudentDto(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "StudentDto{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

StudentService.class

import com.example.crud.model.StudentDto;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

@Service
public class StudentService {
    // 복수의 StudentDto 를 담는 멤버변수 (필드)
    private final List<StudentDto> studentList = new ArrayList<>();
    private Long nextId = 1L;

    public StudentService() {
        createStudent("alex", "alex@gmail.com");
        createStudent("brad", "brad@gmail.com");
        createStudent("chad", "chad@gmail.com");
    }

    // 새로운 StudentDto 를 생성하는 메소드
    public StudentDto createStudent(String name, String email) {
        StudentDto newStudent = new StudentDto(
                nextId, name, email
        );
        nextId++;
        studentList.add(newStudent);
        return newStudent;
    }

    public List<StudentDto> readStudentAll() {
        return studentList;
    }

    // Service에서 단일 StudentDto를 주는 메소드를 만들겁니다
    // 이때 이 메소드가 받을 인자는 무엇일까요?
    public StudentDto readStudent(Long id) {
        for (StudentDto studentDto: studentList) {
            if (studentDto.getId().equals(id))
                return studentDto;
        }

        return null;

//        return studentList
//                .stream()
//                .filter(studentDto -> studentDto.getId().equals(id))
//                .findFirst()
//                .orElse(null);
    }

    // 어떤 학생 데이터를 갱신할 것인지
    // 그 학생의 갱신될 데이터
    public StudentDto updateStudent(Long id, String name, String email){

        // 하나의 StudentDto를 찾아서
        int target = -1;
        // studentList의 크기만큼 반복
        for (int i = 0; i < studentList.size(); i++) {
            // id가 동일한 studentDto 찾았으면
            if (studentList.get(i).getId().equals(id)) {
                // 인덱스 기록
                target = i;
                // 반복 종료
                break;
            }
        }
        // 대상을 찾았다면
        if (target != -1) {
            // name과 email을 바꿔주자
            studentList.get(target).setName(name);
            studentList.get(target).setName(email);
            return studentList.get(target);
        }
        // 대상을 못 찾았다면
        else return null;
    }

    public boolean deleteStudent(Long id) {
        int target = -1;
        // 학생 리스트를 살펴보며
        for (int i = 0; i < studentList.size(); i++) {
            // 대상을 선정한다.
            if (studentList.get(i).getId().equals(id)) {
                target = i;
                break;
            }
        }

        // 검색 성공시
        if (target != -1) {
            studentList.remove(target);
            return true;
        }
        else return false;
    }
}

StudentController

import com.example.crud.model.StudentDto;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class StudentController {
    // StudentService 를 Controller 에서 사용
    private final StudentService studentService;

    public StudentController(
            StudentService studentService) {
        this.studentService = studentService;
    }

    @GetMapping("/create-view")
    public String createView() {
        return "create";
    }

    @PostMapping("/create")
    public String create(
            @RequestParam("name")
            String name,
            @RequestParam("email")
            String email
    ) {
        System.out.println(name);
        System.out.println(email);
        StudentDto studentDto
                = studentService.createStudent(name, email);
        System.out.println(studentDto.toString());
//        return "redirect:/create-view";
        return "redirect:/home";

    }

    @GetMapping("/home")
    public String home(Model model) {
        model.addAttribute(
                "studentList",
                studentService.readStudentAll()
        );
        return "home";
    }

    @GetMapping("/{id}")
    public String read(
            @PathVariable("id") Long id,
            Model model
    ) {
//        studentService.readStudent(id);
        System.out.println(id);
        model.addAttribute(
                "student",
                studentService.readStudent(id)
        );
        return "read";
    }

    @GetMapping("/{id}/update-view")
    public String updateView(
            @PathVariable("id") Long id,
            Model model
    ){
        model.addAttribute(
                "student",
                studentService.readStudent(id)
        );
        return "update";
    }

    @PostMapping("/{id}/update")
    public String update(
            @PathVariable("id") Long id, String name, String email
    ) {
        studentService.updateStudent(id, name, email);
        return String.format("redirect:/%s", id);
    }

    @GetMapping("/{id}/delete-view")
    public String deleteView(
            @PathVariable("id")
            Long id,
            Model model
    ){
        model.addAttribute(
                "student",
                studentService.readStudent(id)
        );
        return "delete";
    }

    @PostMapping("/{id}/delete")
    public String delete(
            @PathVariable("id")
            Long id
    ) {
        studentService.deleteStudent(id);
        // update 때는 데이터가 남아있지만
        // delete 는 돌아갈 상세보기가 없다
        // 그래서 홈으로 돌아간다.
        return "redirect:/home";
    }
}

Read One

read.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>[[${student.id}]]. [[${student.name}]]</h1>
<p>이메일: [[${student.email}]]</p>
<a th:href="@{/{id}/update-view (id=${student.id})}">Update</a>
<a th:href="@{/{id}/delete-view (id=${student.id})}">Delete</a>
<a href="/home">Back</a>
</body>
</html>

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Students Home</title>
</head>
<body>
<h1>Student List</h1>
<div th:if="${studentList.isEmpty()}">
    <p>No students here...</p>
</div>
<div th:unless="${studentList.isEmpty()}" th:each="student: ${studentList}">
    <p>번호: [[${student.id}]]</p>
    <p>이름: <a th:href="@{/{id} (id=${student.id})}">[[${student.name}]]</a></p>
    <p>이메일: [[${student.email}]]</p>
    <hr>
</div>
<a href="/create-view">Create</a>
</body>
</html>

Update

데이터 갱신을 위해서 대상 데이터를 가져오고 갱신할 새로운 데이터로 업데이트 해야한다.

update.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Update</title>
</head>
<body>
  <h1>Update Student</h1>
  <form th:action="@{/{id}/update (id=${student.id})}" method="post">
    <!-- 사용자가 제공할 데이터에 알맞은 Input과 label을 만든다. -->
    <label for="name-input">Name: <input id="name-input" name="name" th:value="${student.name}"></label><br>
    <label for="email-input">Email: <input id="email-input" name="email" th:value="${student.email}"></label><br>
    <input type="submit">
  </form>
<!--  <a href="/create-view">Create</a>-->
  <a th:href="@{/{id} (id=${student.id})}">Back</a>
</body>
</html>

Create, Read 코드와 유사하다.

Delete

상세보기 페이지에서 삭제 여부를 확인하고 삭제 후 home으로 이동하기

delete.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Delete</title>
</head>
<body>
    <h1>Delete [[${student.name}]]?</h1>
    <form th:action="@{/{id}/delete (id=${student.id})}" method="post">
        <!-- create / update 와는 다르게 -->
        <!-- delete 의 경우 추가적인 데이터를 필요로 하지 않는다. -->
        <!-- 그래서 추가적인 input 요소 없이 input type="submit"만 만들면 된다. -->
        <input type="submit" value="Delete">
    </form>
    <a th:href="@{/{id} (id=${student.id})}">Back</a>
</body>
</html>

PathVariable과 RequestParam의 차이

  • PathVariableurl에서 특정 데이터를 지정하는 등 한 가지 값을 받을 수 있다.
    • ex) 학생들 중 id12번인 학생의 마이페이지를 가고 싶다.
    • localhost:8080/mypage/{ 학생의 아이디(12) }
  • RequestParam의 경우 해당 위치에서 사용할 데이터를 담아 보내는 용도이다.
    • ex) 학생 id 12마이페이지에서 오늘 날짜에 출석 체크를 하고 싶다.
    • localhost:8080/mypage/{ 학생의 아이디(12) }?date=20230609&isOk=true

출처 : 멋사 백엔드 5기 11팀 11번가

인사이트 타임

수업 Q&A

Q. StudentService.class 에서 target = -1 인 이유?

원기님의 이짚넘

원기님의 알고리즘 특강

https://www.acmicpc.net/problem/11501

원기님의 MVC.ppt

review

드디어 간단한 모든 기능을 구현해보았다. 하지만 코드만 따라쳤을뿐(영타 실력 상승) 로직을 이해한건 10%도 채 안된것 같다. 이 어려운 것을 원기님께선 이미 마스터를 하셨다는데 정말 놀라운 사람이다. 오늘 인사이트 타임의 주역을 맡아주셨지만 왕초보 돌머리인 나로서는 MVC와 알고리즘을 온전히 이해할 수 없었다.
원기님의 팁으로는 직접 코드를 작성해보는게 실력이 늘 수 있는 가장 효율적인 방법이라고 추천해주셨다. 아마 이 기능을 혼자 구현하려면 지금보다 다섯배는 더 오래 걸릴것 같은데.. 중요한 건 분리 했던 Service와 Controller의 기능을 헷갈리지 않게 구분할 수 있도록 복습을 많이 해야 할 것 같다.

profile
반갑습니다람지
post-custom-banner

0개의 댓글