22/05/20

안은주·2022년 5월 30일
0
💡 Spring 강의 듣고 이해하기!!

[학습내용]


스프링의 구조 (3가지 영역)

  1. Controller - 가장 바깥 부분 요청/응답을 처리함.

    • 클라이언트의 요청을 받음.
    • 요청에 대한 처리는 서비스가 해줄거야~
    • 다시 클라이언트에게 응답을 해줌.
  2. Service - 실제 중요한 작동이 많이 일어나는 부분

    • 사용자의 요구사항을 처리해줌.
    • DB 정보가 필요하면 repository에게 전담.
  3. repository - 가장 안쪽 부분 db와 맞닿아 있음.

    • DB를 관리해줌(연결, 해제, 자원관리)
    • DB의 CRUD 작업을 처리.

웹 동작 방식

  • 웹서비스에서 클라이언트 요청에 서버가 응답하는 방법은 두가지
  1. 데이터로 응답하기

  2. HTML, CSS, javascript 를 한 번에 돌려주기.

근데 우리가 완성된 웹 화면(위에서 2번)말고 “데이터만” 받아오려면 RestController라는걸 이용해야 함.

💡 - Rest는 뭐고 Controller는 뭐지???

Rest는 서버의 응답이 JSON(키:값) 형식임을 나타냄.

즉 서버가 HTML, CSS, javascript를 돌려주는게 아니라 JSON을 돌려준다.

Controller는 사용자가 화면(VIEW)단에서 입력이나 어떤 이벤트를 했을 경우,

그 이벤트에 맞는 화면(VIEW)이나 비즈니스 로직(MODEL)을 실행할 수 있도록 업데이트 해주는 역할을 함.

즉, Rest ControllerJSON형식으로 응답하는 응답기다.

Rest Controller 뜻은 알겠어. 그래서 대체 Controller가 뭔데??

  • 먼저 MVC를 짚어보자면 Model, View, Controller의 의미이며, 스프링이 제공하는 웹 구축을 위한 프레임워크임.

    - Model = 무엇을 할 지에 대한 로직을 담고 비즈니스 로직을 처리하기 위한 역할을 담당.

    - View = 최종 사용자에게 결과를 화면(UI)로 보여주기 위한 역할 즉 사용자에게 시각적으로 보여주는 부분.

    - Controller = 이 두 사이에 있는 컴포넌트로 Model이 데이터를 어떻게 처리할 지 알려주는 역할을 담당.

즉, 스프링 프레임워크에서의 Controller 역할은 다음과 같음.

  1. Data receive -데이터를 수신하고, (요청이 들어오면)
  2. Interpret - 수신한 데이터를 해석해서, (무슨 요청인지)
  3. Validate input data -입력 데이터가 뭐인지 확인. (확인해서)
  4. Update View - 화면 업데이트. (거기에 맞는 응답을 줄 수 있도록.) ex) index.html, login.html
  5. Modify Model - 모델 수정(어플리케이션이 뭘 할건지 정의 DB와 연동하여 사용자가 입력한 데이터나
    사용자에게 출력할 데이터를 다룸.)
💡 Controller를 왜 쓰지??

대규모 서비스로 갈수록 처리해야 할 서비스들이 많아지는데,

이를 하나의 클래스에서 몰아 처리할게 아니라 controller라는 중간제어자 역할을 만들어서

A요청에 대한 것은 A-controller가 B요청에 대한 것은 B-controller가 맡아 필요한 로직 처리를 위한 서비스를 호출. 역할분담이 핵심.

Controller가 무슨 뜻인지도 알겠어.

근데 RestController 와 Controller의 차이는 또 뭔데?

https://dncjf64.tistory.com/288

https://cokes.tistory.com/30

https://dev-coco.tistory.com/84

https://wondongho.tistory.com/76

https://memostack.tistory.com/244 ‼️


Spring 프로젝트 시작하기

  • spring프로젝트 만들 때 Artifact부분은 이 프로젝트의 이름이라는 뜻.
  • dependencies초기 세팅으로는 총 5가지를 가져와야함.
  1. H2 Database = In-memory DB 서버가 작동을 멈추면 데이터가 모두 삭제됨. 연습용으로 딱!

  2. Lombok = @을 사용하여 코드를 절약할 수 있게 해주는거.

    • Lombok은 자바 프로젝트를 진행하는데 거의 필수적으로 필요한 메소드

예를 들어 자주 사용한 getter,setter 또는 생성자 관련해서 자주 쓰이는 코드롤 자동생성해줌으로써 코드를

절약할 수 있게 도와주는 라이브러리.

  1. spring web = 우리는 스프링으로 웹 만들거니까
  2. spring Data JPA = 우리는 JPA로 db 데이터를 다룰거니까.
  3. mysql Driver
  • command+, 누르고 Preference 들어가서 compiler - Annotation Processors에서
    제일 위에 있는 Enable annotation processing 체크
  • Editor - General - Auto Import들어가서 Add unambiguous … 체크.

💥 뭐??? 8080포트가 이미 사용중이라고?? 그럼 죽여버려야지.

  1. 터미널로 들어가서 “야 8080 누가 써!!!!!!!!” → sudo lsof -i :8080
  2. “너 내가 죽여버릴거야” → sudo kill -9 PID

Gradle

  • 보통 개발자들은 다른 사람이 만든 코드를 가져다 쓰는 경우가 많음(의존하는 경우가 많음 )
    다른 사람들이 만든 도구를 내려 받는데 있어서

    javascript — NPM

    python — pip

    Java — mavenCentral, jcenter

    이런걸 통해서 다른 사람의 코드를 다운받고 적용하는걸 굉장히 간편하게 할 수 있음.

그걸 도와주는 녀석이 Gradle

뿐만 아니라 gradle은 이거 말고도 우리가 스프링 프로젝트를 인터넷에 배포할 수 있게 빌드하는걸 도와주기도 함.

  • 쓰는 방법은 인터넷에 maven repository를 검색해서 들어감.
  • maven repository는 java를 쓰는 사용자들이
    다른 사람들이 만들어 놓은 코드를(라이브러리) 검색할 수 있는 장소.

코드를 복사해서 build.gradle 파일 dependencies 부분에 갖다붙임.


H2 웹콘솔 띄우는 방법

src > main > resources > application.propertries에 밑에 코드 삽입.

spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MYSQL

그 다음, 서버 작동시키고 웹에서 localhost:8080/h2-console 을 검색하면 됨.

💥H2 웹 콘솔 연결하다 생긴 삽질 해결법

https://leeeehhjj.tistory.com/52


JPA 가 뭐야??

JPA는 자바 명령어를 SQL 언어로 잘 번역해줘서 자바언어로 DB를 다룰 수 있게 해주는 번역기.

JPA가 없으면 우리가 일일이 SQL언어로 데이터를 조회,삽입,수정,삭제 해야 되는데

이를 자바 언어로도 할 수 있게 해줌.

즉, JPA는 SQL언어를 쓰지 않고도 데이터를 생성, 조회, 수정, 삭제 할 수 있게 해주는 번역기.

JPA는 인터페이스를 통해서만 사용가능.

  • 만약 JPA가 없다면?

→ Sql 썼다 자바썼다 왔다갔다 해야함.

  • JPA가 있다면?
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

→ 설정은 위에 코드 한 줄이면 끝남.

→ 명령도 그냥 자바로 만들면 됨.


Domain, 테이블 설계, 엔티티 생성

👉🏻 Domain.equls(Table(MySQL))

“자, 나는 지금부터 간단한? 게시판을 만들거야.

게시판에 글을 쓰려면 회원 정보가 필요하겠네?

그럼 회원이 있어야겠다. 게시글이랑 댓글도 있어야겠네? 한 번 만들어보자~”

1. 개발 전체를 하나씩 모듈화

2. 각각의 엔티티 분석 (속성, 기능 분석)

회원

  • ID

  • 이름

  • 나이

  • 닉네임 등,,

  • 회원가입

  • 로그인

  • 회원정보 수정

  • 회원 탈퇴

게시글

  • 생성날짜

  • 수정날짜

  • 게시글

  • 제목

  • 글쓴이 등,,

  • 비회원은 작성 불가능, 회원만 작성 가능

  • 제목, 내용, 첨부파일을 올릴 수 있음

  • 작성자는 게시물에 대한 수정과 삭제가 가능함

  • 게시물을 삭제하면, 달려있는 댓글들이 일괄적으로 삭제됨

  • 작성자가 회원탈퇴를 하면, 게시물도 삭제됨

  • 게시물에는 댓글을 달 수 있음

  • (익명 기능도 추가하고 싶지만.. 조금 미루도록 하겠습니다.)

댓글

  • 게시글

  • 작성자

  • 댓글 등,,

  • 게시글에 작성할 수 있음

  • 첨부파일은 불가능하게 만들 것임

  • 댓글은 대댓글을 가질 수 있으나. 대대댓글은 가질 수 없음

    • 즉, 최대 댓글의 깊이가 2임. 아래에서 좀 더 설명하겠습니다.
  • 댓글은 수정과 삭제가 가능하지만, 삭제 시 대댓글이 존재하는 경우 대댓글은 삭제되지 않음.

    • 대댓글이 없는 경우에만 댓글이 완전히 삭제가 됨
    • 대댓글이 남아있는 경우에는 댓글은 "삭제된 댓글입니다" 표시, 대댓글은 그대로 달려있음
    • 그상태에서 대댓글이 모두 사라진다면 댓글 자체를 삭제함
    • 대댓글이 여러개 달려있는 상황에서 중간 대댓글을 지우면, 순서는 유지되고, "삭제된 댓글입니다" 라고 뜸

3. 각각의 테이블 분석 및 설계

  • 각자 테이블이 가지고 있는 의존관계를 표시

👉🏻 DB설계 툴 쓸 수 있는 곳

4. 엔티티 설계

“이제 테이블이랑 분석한 정보 보면서 엔티티 만들어보자!”

Member

@Table(name = "MEMBER")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@AllArgsConstructor
@Builder
public class Member extends BaseTimeEntity {

    @Id @GeneratedValue(strategy = IDENTITY)
    @Column(name = "member_id")
    private Long id; //primary Key

    @Column(nullable = false, length = 30, unique = true)
    private String username;//아이디

    private String password;//비밀번호

    @Column(nullable = false, length = 30)
    private String name;//이름(실명)

    @Column(nullable = false, length = 30)
    private String nickName;//별명

    @Column(nullable = false, length = 30)
    private Integer age;//나이

    @Enumerated(STRING)
    @Column(nullable = false, length = 30)
    private Role role;//권한 -> USER, ADMIN

		@Column(length = 1000)
    private String refreshToken;//RefreshToken

    //== 회원탈퇴 -> 작성한 게시물, 댓글 모두 삭제 ==//
    @OneToMany(mappedBy = "writer", cascade = ALL, orphanRemoval = true)
    private List<Post> postList = new ArrayList<>();

    @OneToMany(mappedBy = "writer", cascade = ALL, orphanRemoval = true)
    private List<Comment> commentList = new ArrayList<>();

    //== 연관관계 메서드 ==//
    public void addPost(Post post){
        //post의 writer 설정은 post에서 함
        postList.add(post);
    }

    public void addComment(Comment comment){
        //comment의 writer 설정은 comment에서 함
        commentList.add(comment);
    }

    //== 정보 수정 ==//
    public void updatePassword(PasswordEncoder passwordEncoder, String password){
        this.password = passwordEncoder.encode(password);
    }

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

    public void updateNickName(String nickName){
        this.nickName = nickName;
    }

    public void updateAge(int age){
        this.age = age;
    }

    public void updateRefreshToken(String refreshToken){
        this.refreshToken = refreshToken;
    }
    public void destroyRefreshToken(){
        this.refreshToken = null;
    }

    //== 패스워드 암호화 ==//
    public void encodePassword(PasswordEncoder passwordEncoder){
        this.password = passwordEncoder.encode(password);
    }

}

POST

import static javax.persistence.CascadeType.ALL;
import static javax.persistence.FetchType.*;
import static javax.persistence.GenerationType.IDENTITY;

@Table(name = "POST")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Post extends BaseTimeEntity {

    @Id @GeneratedValue(strategy = IDENTITY)
    @Column(name = "post_id")
    private Long id;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "writer_id")
    private Member writer;

    @Column(length = 40, nullable = false)
    private String title;

    @Lob
    @Column(nullable = false)
    private String content;

    @Column(nullable = true)
    private String filePath;

    //== 게시글을 삭제하면 달려있는 댓글 모두 삭제 ==//
    @OneToMany(mappedBy = "post", cascade = ALL, orphanRemoval = true)
    private List<Comment> commentList = new ArrayList<>();

    //== 연관관계 편의 메서드 ==//
    public void confirmWriter(Member writer) {
        //writer는 변경이 불가능하므로 이렇게만 해주어도 될듯
        this.writer = writer;
        writer.addPost(this);
    }
    public void addComment(Comment comment){
        //comment의 Post 설정은 comment에서 함
        commentList.add(comment);
    }

    //== 내용 수정 ==//
    public void updateTitle(String title) {
        this.title = title;
    }

    public void updateContent(String content) {
        this.content = content;
    }

    public void updateFilePath(String filePath) {
        this.filePath = filePath;
    }
}

COMMENT

import static javax.persistence.FetchType.LAZY;

@Table(name="COMMENT")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Comment extends BaseTimeEntity {

    @Id @GeneratedValue
    @Column(name = "comment_id")
    private Long id;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "writer_id")
    private Member writer;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "post_id")
    private Post post;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "parent_id")
    private Comment parent;

    @Lob
    @Column(nullable = false)
    private String content;

    private boolean isRemoved= false;

    //== 부모 댓글을 삭제해도 자식 댓글은 남아있음 ==//
    @OneToMany(mappedBy = "parent")
    private List<Comment> childList = new ArrayList<>();

    //== 연관관계 편의 메서드 ==//
    public void confirmWriter(Member writer) {
        this.writer = writer;
        writer.addComment(this);
    }

    public void confirmPost(Post post) {
        this.post = post;
        post.addComment(this);
    }

    public void confirmParent(Comment parent){
        this.parent = parent;
        parent.addChild(this);
    }

    public void addChild(Comment child){
        childList.add(child);
    }

    //== 수정 ==//
    public void updateContent(String content) {
        this.content = content;
    }
    //== 삭제 ==//
    public void remove() {
        this.isRemoved = true;
    }

    @Builder
    public Comment( Member writer, Post post, Comment parent, String content) {
        this.writer = writer;
        this.post = post;
        this.parent = parent;
        this.content = content;
        this.isRemoved = false;
    }

}

확인


Repository (DAO - Data Access Object)

👉🏻 Repository .equls(SQL)

  • JPA코드를 사용하는 것은 repository에 있는 코드를 사용하는 것과 같음.

  • Repository는 클래스의 한 역할에 해당하는데, 데이터에 접근할 때 사용하는 하나의 도구(DAO).

  • .find / .save / .delete 처럼 db를 다룰 수 있는 언어를 제공함.

  • repository는 엔티티별로 분리.

**→ Course Table을 다루기 위한 CourseRepository를 만들었음.**
  • JpaRepository를 상속받음으로써 사용가능해짐.


)

“Course 테이블에 접근할 수 있는 repository를 만들거야 이름은 CourseRepository라고 정했어!

그리고선 JpaRepository를 상속받아야 돼 왜냐구??

상속을 받아야 JpaRepository 안에 있는 Jpa코드를 쓸 수있지!! find / .save / .get 이런거 말이야~

근데 쓸 때 조건이 있어! 뭐냐면 JpaRepository<대상으로 지정할 엔티티, 해당 엔티티의 PK의 타입>이야.

알겠지??”


CRUD

  • CRUD 는 정보관리의 기본 기능

→ 생성 create

→ 조회 read

→ 변경 update

→ 삭제 delete


Service

💡 update, delete로 넘어가기 전에 , 다루어야 하는 개념!!
  • repository는 가장 안쪽 부분이라서 DB와 맞닿아 있고, — DB에 접근하는 녀석! DAO
  • Controller는 요청이 들어오면 요청을 받아주는 응답기 역할을 함. — 요청이 들어오면 요청을 받아주는 녀석!!
  • DB를 꺼내오는 녀석 repository와 요청에 응답을 하는 녀석 Controller 사이에는 연결고리가 필요함.
  • 연결해주는 그걸 Service 라고 함 — 들어온 요청을 어떻게 처리할지 구현해주는 녀석!!
  • Update는 외부에서 요청이 들어옴. 그래서 repository까지 전달을 해줘야 함.
  • 그럴 땐 중간에 Service를 활용함.
  • Update는 서비스 부분에 작성함.

⭐️ https://velog.io/@codren/Domain-Repository 이따 정리‼️‼️‼️‼️


Entity 란?

  • Entity클래스는 DB의 테이블에 존재하는 Column들을 필드로 가지는 객체.
  • Entity는 DB의 테이블과 1대 1로 대응되며, 때문에 테이블이 가지지 않는 컬럼을 필드로 가져서는 안됨.
  • Entity 클래스는 다른 클래스를 상속받거나 인터페이스의 구현체여서는 안된다.

DTO (Data Transfer Object)

  • 이름과 같이 계층 간 데이터 교환을 위해 사용하는 객체다.
  • 여기서 말하는 계층이란 보통 View - Controller - Service - DAO와 같은 계층을 말한다.
  • DTO는 그저 계층간 데이터 교환이 이루어 질 수 있도록 하는 객체이기 때문에,
    특별한 로직을 갖지 않는 순수한 데이터 객체여야 함.
  • DB에서 꺼낸 값을 DTO에서 임의로 조작할 필요가 없기 때문에 DTO에는 Setter를 만들 필요가 없고,
    생성자에서 값을 할당.
  • “VO”와 혼용되어 쓰이나, 이는 보통 DTO를 지칭하는 말이다.
  • VO 는 Setter는 없고 Getter만 있는 형태! (즉, read-only)
  • DTO는 데이터를 담을 private 변수와 그 변수를 조작할 수 있는 Getter, Setter 메소드로 구성돼있음.

👉🏻 참고자료 :https://velog.io/@gudonghee2000/DTO


Entitiy와 DTO를 분리하는 이유가 뭐야?

  • Entity 값이 변하면 Repository클래스의 Entity Manager의 flush가 호출될 때 DB에 값이 반영됨.
  • 이는 다른 로직들에게도 영향을 미침.
  • 때문에 데이터의 변경이 많은 DTO클래스를 분리해줘야 함.
  • 후, 그니까 뭐냐면 자바의 Domain 객체를 바로 접근하지 않기 위해서야!!!! 막 맘대로 테이블 조작하면 위험하잖아? 그래서 그냥 한 단계 더 거쳐가게 하는거야~!@

🗒 MEMO

💡 홍삼 마시면서 몸 챙기기. 뭐든 건강해야 의미가 있다!

회고🤔


드디어 주특기 스프링을 공부하는 주가 시작됐다.
자바를 알면 스프링은 저절로 다 알게 되는건 줄 알았는데,,
아니다,,
자바 따로 스프링 따로 공부하는거다,,
하,,ㅎ 공부할거 많아서 너무 행복하다,,ㅎㅎ

profile
우당탕탕 개발일지🤣

0개의 댓글