[내배캠 Java 6기] Spring 주특기 입문 2주차 강의(1 ~ 7)

ekkkyo_j·2024년 8월 16일

[내배캠 Java 6기] TIL

목록 보기
21/38

3. IOC(제어의 역전), DI(의존성 주입)

📌 IoC
: 설계 원칙 => Inversion of Control, 제어의 역전
프로그램 제어 흐름의 주도권을 외부로 넘겨주는 것

📌 DI
: 디자인 패턴 => Dependency Injection, 의존성 주입
객체가 필요한 종속성을 외부에서 주입해주는 것을 “의존성 주입”

=> IoC는 느슨한 결합을 만들기 위한 틀이고, 그 과정으로 DI를 사용한다.

📌 의존성이 높은 코드

public class Consumer {

    void eat() {
        Chicken chicken = new Chicken();
        chicken.eat();
    }

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

class Chicken {
    public void eat() {
        System.out.println("치킨을 먹는다.");
    }
}

Consumer가 Chicken을 만들고 있음. 따라서, 제어의 흐름은 Consumer에서 Chicken이다.

📌 생성자를 통한 주입

public class Consumer {

	Food food;
	public Consumer (Food food) {
    	this.food = food;
    }
    
    void eat() {
    	this.food.eat();
    }
    
    public static void main(String[] args) {
    	Consumer consumer1 = new Consumer(new Chicken());
        consumer1.eat();
        
        Consumer consumer2 = new Consumer(new Pizza());
        consumer2.eat();
    }
}

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("피자를 먹는다.");
    }
 }

main은 Consumer class 밖임. 따라서, 밖에서 Food가 만들어지고 Consumer에게 전달만 된다. 따라서, 제어의 흐름이 Food에서 Consumer이다. 따라서, 이 코드는 IoC 설계 원칙을 따르고, interface Food를 사용하여 DI 디자인 패턴 형태를 가진다.

4. 메모장 프로젝트의 IoC & DI

코드의 중복성을 줄이고, Ioc, DI를 구현한다.
MemoController.java

package com.sparta.memo.controller;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.service.MemoService;
import org.springframework.web.bind.annotation.*;

import java.util.List;

// Dispatcherservlet이 내부적으로 Controller를 사용함
// Controller도 @Component이다. bean 객체이다.
@RestController
@RequestMapping("/api")
public class MemoController {
    // MemoController -> MemoService -> MemoRepository 강한 결함 약하게 만들기
    private final MemoService memoService;

    // JDBC DB 초기화 <-final 이므로
    public MemoController(MemoService memoService) { // 외부에서 만들어진 memoService를 필드에 주입
        this.memoService = memoService;
    }

    @PostMapping("/memos")
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
        // Controller의 메서드와 service의 메서들 이름을 일치시켜라
        return memoService.createMemo(requestDto);

    }

    @GetMapping("/memos")
    public List<MemoResponseDto> getMemos() {
        return memoService.getMemos();

    }

    @PutMapping("/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        return memoService.updateMemo(id, requestDto);
    }

    @DeleteMapping("/memos/{id}")
    public Long deleteMemo(@PathVariable Long id) {
        return memoService.deleteMemo(id);
    }
}

MemoService.java

package com.sparta.memo.service;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.List;

@Service // @Component 포함
// MemoService를 memoService로 bean으로써 IoC container에 등록
// @Componet가 붙어있는 class 내에서만 @Autowired를 쓸 수 있다.
// @RequiredArgsConstructor // // (4). Lombok 주입 방법
public class MemoService {

    // JDBC DB 생성
    private final MemoRepository memoRepository;

    // JDBC DB 초기화 <-final 이므로

    // (1). 생성자 주입 방법 (최고)
    // memoRepository는 final이기 때문에 final은 생성자를 통해서만 초기화가 가능하다.
    public MemoService(MemoRepository memoRepository) { // 외부에서 만들어진 memoRepository를 필드에 주입
        this.memoRepository = memoRepository;
    }

    // (5). IoC Container에 직접 접근하기
//    public MemoService(ApplicationContext context) {
//        (5)-1. 'Bean' 이름으로 찾기
//        MemoRepository memoRepository = (MemoRepository) context.getBean("memoRepository");
//
//        (5)-2. 'Bean' 클래스 형식으로 찾기
//        MemoRepository memoRepository = context.getBean(MemoRepository);
//        this.memoRepository = memoRepository;
//    }


    // (2). 메서드 주입 방법
    // final 빼주기: private MemoRepository memoRepository;
//    @Autowired
//    public void setDi(MemoRepository memoRepository) {
//        this.memoRepository = memoRepository;
//    }

    // (3). 필드 주입 방법 (추천 X)
    // @Autowired
    // private MemoRepository memoRepository;


    public MemoResponseDto createMemo(MemoRequestDto requestDto) {
        // RequestDto -> Entity
        Memo memo = new Memo(requestDto);

        // DB 저장
        Memo saveMemo = memoRepository.save(memo);

        // Entity -> ResponseDto
        MemoResponseDto memoResponseDto = new MemoResponseDto(memo);

        return memoResponseDto;
    }

    public List<MemoResponseDto> getMemos() {
        // DB 조회
        return memoRepository.findAll();

    }

    public Long updateMemo(Long id, MemoRequestDto requestDto) {

        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = memoRepository.findById(id);
        if(memo != null) {
            // memo 내용 수정
            memoRepository.update(id, requestDto);
            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }



    public Long deleteMemo(Long id) {

        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = memoRepository.findById(id);
        if(memo != null) {
            // memo 삭제
            memoRepository.delete(id);

            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }
}

MemoRepository.java

package com.sparta.memo.repository;

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

@Repository // @Component 포함
//@Component
// MemoRepository를 memoRepository로 bean으로써 IoC container에 등록
public class MemoRepository {
    private final JdbcTemplate jdbcTemplate;

    // @Autowired 매개변수가 여러 개면 써줘야됨, 1개면 생략가능
    public MemoRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }


    public Memo save(Memo memo) {
        // DB 저장
        KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체

        String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)"; // 값은 동적이므로 ?, ?
        jdbcTemplate.update( con -> {
                    PreparedStatement preparedStatement = con.prepareStatement(sql,
                            Statement.RETURN_GENERATED_KEYS);

                    preparedStatement.setString(1, memo.getUsername());
                    preparedStatement.setString(2, memo.getContents());
                    return preparedStatement;
                },
                keyHolder);

        // DB Insert 후 받아온 기본키 확인
        Long id = keyHolder.getKey().longValue();
        memo.setId(id);

        return memo;
    }

    public List<MemoResponseDto> findAll() {
        // DB 조회
        String sql = "SELECT * FROM memo";

        return jdbcTemplate.query(sql, new RowMapper<MemoResponseDto>() {
            @Override
            public MemoResponseDto mapRow(ResultSet rs, int rowNum) throws SQLException {
                // SQL 의 결과로 받아온 Memo 데이터들을 MemoResponseDto 타입으로 변환해줄 메서드
                Long id = rs.getLong("id");
                String username = rs.getString("username");
                String contents = rs.getString("contents");
                return new MemoResponseDto(id, username, contents);
            }
        });
    }

    public void update(Long id, MemoRequestDto requestDto) {
        String sql = "UPDATE memo SET username = ?, contents = ? WHERE id = ?";
        jdbcTemplate.update(sql, requestDto.getUsername(), requestDto.getContents(), id);
    }

    public void delete(Long id) {
        String sql = "DELETE FROM memo WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }

    public Memo findById(Long id) {
        // DB 조회
        String sql = "SELECT * FROM memo WHERE id = ?";

        return jdbcTemplate.query(sql, resultSet -> {
            if(resultSet.next()) {
                Memo memo = new Memo();
                memo.setUsername(resultSet.getString("username"));
                memo.setContents(resultSet.getString("contents"));
                return memo;
            } else {
                return null;
            }
        }, id);
    }



}

5. IoC Container와 Bean

📌 Bean
: Spring이 관리하는 객체

📌 IoC Container
: Bean을 담아둔 컨테이너

📌 Bean 등록 방법
:‘Bean’으로 등록하고자하는 클래스 위에 @Component.

@Component
public class MemoService { ... }

📌 Bean 사용 방법
: @Autowired

  1. Field 위에 @Autowired
@Component
public class MemoService {
		
    @Autowired
    private MemoRepository memoRepository;
		
		// ...
}
  1. 'Bean'을 주입할 때 사용할 메서드 위에 @Autowired
@Component
public class MemoService {

    private final MemoRepository memoRepository;

    @Autowired
    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }
		
		// ...
}
  1. Lombok의 @RequiredArgsConstructor 사용
@Component
@RequiredArgsConstructor // final로 선언된 멤버 변수를 파라미터로 사용하여 생성자를 자동으로 생성합니다.
public class MemoService {

    private final MemoRepository memoRepository;
    
//    public MemoService(MemoRepository memoRepository) {
//        this.memoRepository = memoRepository;
//    }

		...

}

📌 3 layered annotation
1. @Controller, @RestController
2. @Service
3. @Repository

6. JPA란 무엇인가?

📌 JPA
: Java Persistence API
자바 ORM 기술에 대한 표준 명세

반복적이고 번거로운 애플리케이션 단에서의 SQL 작업을 줄여주기 위해서 ORM(객체 관계 매핑)기술들이 등장 하게 됩니다.

7. Entity 이해하기

📌 Entity
: JPA에서 관리되는 클래스 즉, 객체를 의미
Entity 클래스는 DB의 테이블과 매핑되어 JPA에 의해 관리

package com.sparta.entity;

import jakarta.persistence.*;

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
public class Memo {
    @Id // Entity를 구분해주는 primary key 이므로 필수적으로 있어야함
    @GeneratedValue(strategy = GenerationType.IDENTITY) // Auto increament
    private Long id;

    // nullable: null 허용 여부
    // unique: 중복 허용 여부 (false 일때 중복 허용)
    @Column(name = "username", nullable = false, unique = true)
    private String username;

    // length: 컬럼 길이 지정
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;
}
profile
게으르지만 성실히 공부하기^_^

0개의 댓글