61일차 (2) - 스프링 (3 Tier Architecture + 성적정보 프로그램 DTO 수정 및 Service 추가)

Yohan·2024년 5월 20일
0

코딩기록

목록 보기
91/156
post-custom-banner

3 Tier Architecture

Controller-Service-Repository 패턴

Controller

  • 웹 요청을 받아서 해당 요청을 처리할 Service를 호출 -> 그 결과를 다시 View에 전달한다. 즉, Controller는 클라이언트로부터의 입력을 처리하며, HTTP 요청에 대한 응답을 생성

Repository

  • Repository는 데이터에 접근하는 역할.
    -> 데이터베이스나 파일, 메모리 등의 다양한 데이터 저장소에 접근하는 기능을 제공

Service

  • 컨트롤러와 레포지토리 사이에 위치하여 중간 처리를 담당
    -> Service는 비즈니스 로직을 수행
    Service는 Repository를 사용하여 데이터에 접근하고, 그 결과를 다시 Controller에 전달. Service는 보통 여러 개의 Repository를 사용하여 데이터를 처리
  • 초록색 부분처럼 요청하고 응답을 생성하는 부분을 제외한 비즈니스 로직 부분을 service에서 담당한다.

성적정보 프로그램 수정사항

  • ScoreListResponseDto
    -> 화면에 보여질 Score 리스트에 대해 데이터 필터링을 진행했다.
@Getter
public class ScoreListResponseDto {

    private long stuNum;
    private String maskingName; // 첫글자 빼고 모두 *처리
    private double average;
    private String grade;

    // Entity를 DTO로 변환
    public ScoreListResponseDto(Score s) {
        this.stuNum = s.getStuNum();
        this.maskingName = makeMaskingName(s.getStuName());
        this.average = s.getAverage();
        this.grade = s.getGrade().toString();
    }

    private String makeMaskingName(String stuName) {
        char firstLetter = stuName.charAt(0);
        String maskedName = "" + firstLetter;
        for (int i = 0; i < stuName.length() - 1; i++) {
            maskedName += "*";
        }
        return maskedName;
    }
}
  • ScoreDetailResponseDto
    -> 화면에 보여질 자세한 성적정보에 대한 데이터를 모아놓은 객체
    • rank와 count는 등수와 전체 인원을 조회하는 findRankByStuNum메서드를 통해 값을 대입
@Getter @Setter
public class ScoreDetailResponseDto {

    // 상세정보 화면을 렌더링하기 위한 데이터
    private long stuNum;
    private String stuName;
    private int kor, eng, math, total;
    private double average;
    private String grade;
    private int rank, totalCount; // 석차와 총 학생수

    public ScoreDetailResponseDto(Score s, int rank, int count) {
        this.stuNum = s.getStuNum();
        this.stuName = s.getStuName();
        this.kor = s.getKor();
        this.eng = s.getEng();
        this.math = s.getMath();
        this.total = s.getTotal();
        this.average = s.getAverage();
        this.grade = s.getGrade().toString();
        this.rank = rank;
        this.totalCount = count;
    }
}

  • ScoreModifyRequestDto
    -> 수정해야하는 부분을 포장하는 객체
    • RequestDto 이므로 입력받아야 하는 부분을 나타냄, Score 클래스에 생성자를 만든다.
@Getter @Setter @ToString
public class ScoreModifyRequestDto {

    private long stuNum;
    private int kor, eng, math;
}

  • updateScore
    -> 저장소에 국영수 점수 수정하기
    • 주의해야 할 부분은 국,영,수 점수가 수정되면 평균, 총점 등급 등도 같이 수정되므로 sql에 포함시켜줘야함!
    // 국영수 점수가 바뀌면? 평균 총점 등급도 바뀌게 되는 것에 주의
    @Override
    public boolean updateScore(Score s) {
        String sql = "UPDATE tbl_score " +
                "SET kor = ?, eng = ?, math = ?, " +
                "total = ?, average = ?, grade = ? " +
                "WHERE stu_num = ?";
        return template.update(sql, s.getKor(), s.getEng(), s.getMath(),
                s.getTotal(), s.getAverage(), s.getGrade().toString(), s.getStuNum()) == 1;
    }

ScoreService

  • @Service 라는 어노테이션 사용 - @Component + Service라고 알려주는 뜻이 동시에 들어감
  • Controller에는 요청과 응답만 처리하고 Dto를 통한 데이터 가공이나 필터링 등을 진행해서 다시 Controller로 보내준다.
/*
    컨트롤러와 레파지토리사이에 위치하여
    중간 처리를 담당

    - 트랜잭션 처리, 데이터 가공 처리...

    - 의존 관계
    Controller -> Service -> Repository
 */
 
@RequiredArgsConstructor
@Service
public class ScoreService {

    private final ScoreRepository repository;

    // 목록 조회 중간처리
    // - DB에서 조회한 성적정보 목록은 민감한정보를 모두 포함하고 있는데
    //   이를 컨트롤러에게 직접 넘기면 보안상 불필요한 정보까지 화면으로
    //   넘어갈 수 있기 때문에 숨길건 숨기고 뺄건 빼는 데이터가공을 처리한다.
    public List<ScoreListResponseDto> getList(String sort) {
        List<Score> scoreList = repository.findAll(sort);
        return scoreList.stream()
                .map(s -> new ScoreListResponseDto(s))
                .collect(Collectors.toList());
    }

    // 저장 중간처리
    public boolean insert(ScorePostDto dto) {
        return repository.save(new Score(dto));
    }

    // 삭제 중간처리
    public boolean deleteScore(long stuNum) {
        return repository.delete(stuNum);
    }

    // 개별조회 중간처리 (rank, count 반영)
    public ScoreDetailResponseDto retrieve(long stuNum) {

        Score score = repository.findOne(stuNum);
        int[] result = repository.findRankByStuNum(stuNum);

        ScoreDetailResponseDto dto = new ScoreDetailResponseDto(score, result[0], result[1]);

        return dto;
    }

    // 수정 완료를 위해 서비스 클래스에서
    // dto를 entity로 변환
    public void update(ScoreModifyRequestDto dto) {

        repository.updateScore(new Score(dto));
    }
}

ScoreController

  • Service 클래스에서 비즈니스 로직을 처리해준 덕분에 Controller 코드가 굉장히 짧아졌다는 것을 알 수 있다.
  • 의존객체가 Repository -> Service로 교체
  • Service에서 처리된 데이터로 요청을 하거나 화면으로 보낸다.
@Controller
@RequestMapping("/score")
@RequiredArgsConstructor
public class ScoreController {

    // 의존객체 설정
    private final ScoreService service;

    @GetMapping("/list")
    public String list(@RequestParam(defaultValue = "num") String sort, Model model) {

        List<ScoreListResponseDto> dtos = service.getList(sort);
        model.addAttribute("sList", dtos);

        return "score/score-list";
    }

    @PostMapping("/register")
    public String register(ScorePostDto dto) {

        // service를 거쳐서 데이터베이스에 저장
        service.insert(dto);

        // 다시 조회로 돌아가야 저장된 데이터를 볼 수 있음
        // 포워딩이 아닌 리다이렉트로 재요청을 넣어야 새롭게 디비를 조회
        return "redirect:/score/list";
    }

    @GetMapping("/remove")
    public String remove(@RequestParam("sn") long stuNum) {

        service.deleteScore(stuNum);
        return "redirect:/score/list";
    }

    @GetMapping("/detail")
    public String detail(long stuNum, Model model) {

        // 1. 상세조회를 원하는 학번을 읽기
        // 2. DB에 상세조회 요청
        // 3. DB에서 조회한 회원정보 JSP에게 전달
        // 4. rank 조회
        ScoreDetailResponseDto dto = service.retrieve(stuNum);
        model.addAttribute("s", dto);

        return "score/score-detail";
    }


    // 수정 화면 열기 요청
    // detail에 있던 데이터를 들고옴 (조회)
    @GetMapping("/modify")
    public String modify(long stuNum, Model model) {

        ScoreDetailResponseDto dto = service.retrieve(stuNum);
        model.addAttribute("s", dto);
        return "score/score-modify";
    }

    // 수정 데이터 반영 요청
    @PostMapping("/modify")
    public String modify(ScoreModifyRequestDto dto) {

        // 1. 수정을 원하는 새로운 데이터 읽기 (국영수 점수  + 학번)
//        System.out.println("dto = " + dto);
        // 2. 서비스에게 수정 위임
        service.update(dto);

        return "redirect:/score/detail?stuNum=" + dto.getStuNum();
    }
}

국영수 점수 수정 (modify)

  • ScoreController
    -> 수정화면을 열었을 때 detail에서 조회되었던 이름,국,영,수 점수에 대한 정보가 그대로 넘어가야 하므로 retrieve메서드를 가져오고 model로 jsp에 쏴준다.

  • 수정 데이터를 반영 (입력받은 것들을 수정)

  • score-detail.jsp
    -> 누른 사람의 정보가 떠야하므로 학번(stuNum)을 받는다.

  • score-modify.jsp

profile
백엔드 개발자
post-custom-banner

0개의 댓글