본문 바로가기

개발 일지/TIL

[ #22 ] TIL

✏️ 0516     


Spring 입문 2주차 강의

수준별 학습: 스탠다드-이론반 OT
Spring 개인 과제 (1~ 5단계)


Spring 입문_2주차

1주차 내용 적은 것 같다고 했던 과거의 나...반성해라

머리털 뽑히는 줄 알았네

 

3 Layer Architecture

: 서버 처리 과정을 크게 Controller, Service, Repository 3개로 분리

 


Controller
- 클라이언트의 요청을 받습니다.
- 요청에 대한 로직 처리는 Service에게 전담
- Service에서 처리 완료된 결과를 클라이언트에게 응답

Service
- 사용자의 요구사항을 처리 ('비즈니스 로직') 하는 실세 중에 실세
- DB 저장 및 조회가 필요할 때는 Repository에게 요청

Repository
- DB 관리 (연결, 해제, 자원 관리)
- DB CRUD 작업을 처리

 

IoC(제어의 역전), DI(의존성 주입)

 

IoC, DI는 객체지향의 SOLID 원칙 그리고 GoF 의 디자인 패턴과 같은 설계 원칙 및 디자인 패턴

DI 패턴을 통해서 IoC 설계 원칙을 이루어낸 것


DI라는 디자인 패턴을 통해서 IoC라는 설계 원칙을 지킨것

 

  • 좋은 코드를 위한 Spring의 IoC와 DI
    • 논리 간단
    • 중복 제거, 표현 명확
    • 처음 보는 사람도 쉽게 이해 & 수정 가능
    • 의존성 최고화
    • 새로운 기능 추가 시 구조 변경 X
    • ....

 

의존성 (DI)

DI 를 사용하기 위해서는 객체 생성이 우선

다리가 다쳐서 목발을 사용하기 된다면 걸을 때 목발에 의존하고 있는 것과 같은 것!

 

강하게 결합되어 되어있는 코드를 Interface 를 활용해서 해결이 가능하다

Interface 다향성의 원리 사용

=> 약한 결합 및 약한 의존성

public class Consumer {
    void eat(Food food) {
        food.eat();
    }
    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.eat(new Chicken());
    }
}

interface Food {
    void eat();
}
class Chicken implements Food{
    @Override
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}
class Pizza implements Food{
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}

 

 

주입

=> 여러 방법을 통해 필요로 하는 객체를 해당 객체에 전달하는 것

public class Consumer {
    // 필드에 직접 주입
    Food food;
	
    void eat() {
        this.food.eat();
    }

    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        consumer.food = new Chicken();
        consumer.eat();
		
        // set 메서드 주입
        consumer.setFood(new Pizza());
        consumer.eat();
    }
}
// 이하 동일

 

public class Consumer {
    Food food;
	
    // 생성자
    public Consumer(Food food) {
        this.food = food;
    }
    void eat() {
        this.food.eat();
    }
    
    public static void main(String[] args) {
        Consumer consumer = new Consumer(new Chicken());
        consumer.eat();
    }
}

 

Food 를 Consumer 에 포함 시키고 Food 에 필요한 객체를 주입받아 사용하는 것

 

필드에 직접 주입, set 메서드를 통해 주입, 생성자를 통한 주입

다양한 방식으로 필요한 객체를 주입받아 사용할 수 있다

 

 

제어의 역전 (IoC)

위의 코드를 기반으로 설명하면 Consumer 가 직접 Food 를 만들어 먹었기 때문에

새로운 Food 를 만들려면 추가적인 요리준비(코드변경) 이 필요했다

제어의 흐름 => Consumer → Food

 

주입을 통해 Food 를 Consumer 에게 전달해주는 식으로 변경함으로써

Consumer 는 추가적인 요리준비(코드변경)없이 어느 Food 가 되었든 전부 먹을 수 있게 되었다

제어의 흐름 => Food → Consumer

 

 

JPA (ORM)

 

  • DB 를 직접 다룰 때의 문제점
    • 직접 DB로 접속해서 SQL 호출
    • 애플리케이션에서 SQL 작성 => SQL 직접 수정 필요
    • JDBC 를 사용해서 직접 실행
    • SQL 결과를 객체로 직접 변환
    •  

새 필드 값이 추가가 되면 관련된 모든 코드들의 수정이 필요하고 작업이 굉장히 많아진다

ORM 이 없는 환경에서는 백엔드 개발자가 비즈니스 로직 개발보다, 

SQL 작성 및 수정에 더 많은 노력을 들여야 했다

 

ORM ( Object-Relational Mapping )
객체와 DB의 관계를 매핑 해주는 도구

 

 

Objec : 객체

Relational : 관계형

 

반복적이고 번거로운 애플리케이션 단에서의 SQL 작업을 줄여주기 위한 기술

자바의 클래스와 DB 의 데이터를 직접 매핑하기 위한 코드 변경 작업을

ORM 을 사용하면 자동으로 처리해준다

 

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Getter
@Setter
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
@NoArgsConstructor
public class Memo extends Timestamped {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "username", nullable = false)
    private String username;
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;
}

public interface MemoRepository extends JpaRepository<Memo, Long> {
    List<Memo> findAllByOrderByModifiedAtDesc();
    List<Memo> findAllByContentsContainsOrderByModifiedAtDesc(String keyword);
}

 

  • 장점
    • 객체지향적인 코드로 인해 더 직관적이고 로직에 집중 가능
    • 재사용 및 유지보수의 편리성 증가
    • 코드의 가독성 향상
    • DBMS 에 대한 종속성 감소
  • 단점
    • 대량의 데이터를 처리하는 경우 성능 문제가 발생할 가능성
    • 완벽한 ORM 으로만 서비스를 구현하기 어려움
    • 사용하기는 편하지만 설계는 매우 신중하게
    • 프로젝트의 복잡성이 커질 경우 난이도 또한 올라갈 수 있다

 

Entity

 

 

Entity
JPA 에서 관리되는 클래스
즉, 객체를 의미

 

 

 

Entity 클래스는 DB의 테이블과 매핑되어 JPA 에 의해 관리

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정 (name = "memo") <= 지정할 클래스 이름도 설정가능
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
public class Memo {
    @Id  // 테이블의 기본키 (PK)
 // @GeneratedValue(strategy = GenerationType.IDENTITY)  // auto increment 설정
    private Long id;

    // 필드의 이름과 테이블 컬럼의 이름을 매핑시켜준다
    // 기본적으로 필드의 이름이 디폴트가 된다
    // nullable: null 허용 여부 (false 일때 null 불가 / true 일때 null 허용)
    // unique: 중복 허용 여부 (false 일때 중복 허용 / true 일때 중복 불가)
    @Column(name = "username", nullable = false, unique = true)
    private String username;

    // length: 컬럼 길이 지정
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;
}

 

  • @Entity : JPA가 관리할 수 있는 Entity 클래스로 지정
    • name = "Memo" : Entity 클래스 이름 지정
    • JPA가 Entity 클래스를 인스턴스화 할 때 기본 생성자를 사용하기 때문에
      반드시 현재 Entity 클래스에서 기본 생성자가 생성되고 있는지 확인
  • @Table : 매핑할 테이블을 지정
    • name = "memo" : 매핑할 테이블의 이름을 지정 ( default: Entity 명 )
  • @Column : 필드의 이름과 테이블 컬럼의 이름을 매핑
    • name = "username" : 필드와 매핑할 테이블의 컬럼 지정
    • nullable = false : 데이터의 null 값 허용 여부 지정
    • unique = true : 데이터의 중복 값 허용 여부 지정
    • length = 500 : 데이터 값(문자)의 길이에 제약조건 ( default: 255 )
  • @Id : 테이블의 기본 키를 지정
    • 영속성 컨텍스트에서 Entity를 구분하고 관리할 때 사용되는 식별자 역할 수행
    • 기본 키 즉, 식별자 값을 넣어주지 않고 저장하면 오류 발생
    • @Id 옵션만 설정하면 기본 키 값을 개발자가 직접 확인하고 넣어줘야 하는 불편함 발생
  • @GeneratedValue : 옵션을 추가하면 기본 키 생성을 DB에 위임 가능
  • (strategy = GenerationType.IDENTITY) 
  • id bigint not null auto_increment : auto_increment 조건이 추가된 것
  • 해당 옵션을 추가해주면 개발자가 직접 id 값을 넣어주지 않아도 자동으로 순서에 맞게 기본 키 추가

 

개인과제 (1~5단계)

죽...여...줘......

 

시작 전 확인할 것

- git init

- build.gradle 파일 (의존성)

- application.properties 파일

 

초반에 제대로 되는지 확인 안하고 실행하니까 어디서부터 꼬여있는지 모르겠어서 그냥 리셋시켰다

꼭 무언갈 추가하면 이상이 없는지 run 해서 잘 작동하는지 확인하기

(근데 대부분 오타였다)

 

예전에 스프링 배웠을 때도 코드가 점점 길어지면 길어질수록

어디서 잘못됐는지 확인하는 것에 시간이 오래 걸리기 때문에 되도록 자주 확인하는 것이 좋은 것 같다...

'개발 일지 > TIL' 카테고리의 다른 글

[ #24 ] TIL  (2) 2024.05.20
[ #23 ] TIL  (3) 2024.05.19
[ #21 ] TIL  (2) 2024.05.14
[ #20 ] TIL  (4) 2024.05.13
[ #19 ] TIL  (2) 2024.05.10