3주차 시작!
[개발자 입문]웹개발의 봄, Spring - 3주차 | Notion
PDF 파일
teamsparta.notion.site
Ctrl + Alt + O : import 옵티마이저
Ctrl + Alt + L : 코드 정렬
Ctrl + / : 다 줄의 코드 주석처리
Ctrl + E : 탭 변경
Alt + Ins : 추가
3-1 3Layer Architecture
서버에서의 처리 과정이 대부분 비슷하다는 걸 깨닫고,
처리 과정을 크게 Controller, Service, Repository 3개로 분리
Controller의 역할을 나누는 것
3-2 역할 분리하기
패키지와 자바클래스 만들기
Ctrl + Alt + O : import 옵티마이저
1. Service로 요청과 데이터 보내기
src > main > java > com.sparta.memo > controller > MemoController
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.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api")
public class MemoController {
//복사
private final JdbcTemplate jdbcTemplate;
public MemoController(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
//PostMappin 변경
@PostMapping("/memos")
public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
MemoService memoService = new MemoService(jdbcTemplate); //인스턴스화
return memoService.createMemo(requestDto); //컨트롤러와 서비스의 매소드를 똑같이 하기(협업할 때를 위해)
}
@GetMapping("/memos")
public List<MemoResponseDto> getMemos() {
MemoService memoService = new MemoService(jdbcTemplate);
return memoService.getMemos();
}
@PutMapping("/memos/{id}")
public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
MemoService memoService = new MemoService(jdbcTemplate);
return memoService.updateMemo(id, requestDto);
}
@DeleteMapping("/memos/{id}")
public Long deleteMemo(@PathVariable Long id) {
MemoService memoService = new MemoService(jdbcTemplate);
return memoService.deleteMemo(id);
}
}
src > main > java > com.sparta.memo > service > MemoService
package com.sparta.memo.service;
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 java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
public class MemoService {
private final JdbcTemplate jdbcTemplate;
public MemoService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
// 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);
// Entity -> ResponseDto
MemoResponseDto memoResponseDto = new MemoResponseDto(memo);
return memoResponseDto;
}
public List<MemoResponseDto> getMemos() {
// 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 Long updateMemo(Long id, MemoRequestDto requestDto) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findById(id);
if(memo != null) {
// memo 내용 수정
String sql = "UPDATE memo SET username = ?, contents = ? WHERE id = ?";
jdbcTemplate.update(sql, requestDto.getUsername(), requestDto.getContents(), id);
return id;
} else {
throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
}
}
public Long deleteMemo(Long id) {
// 해당 메모가 DB에 존재하는지 확인
Memo memo = findById(id);
if(memo != null) {
// memo 삭제
String sql = "DELETE FROM memo WHERE id = ?";
jdbcTemplate.update(sql, id);
return id;
} else {
throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
}
}
private 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);
}
}
2. Repository에 DB역할 분리
src > main > java > com.sparta.memo > service > MemoService
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 org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class MemoService {
private final JdbcTemplate jdbcTemplate;
public MemoService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
// DB 저장
MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
Memo saveMemo = memoRepository.save(memo);
// Entity -> ResponseDto
MemoResponseDto memoResponseDto = new MemoResponseDto(memo);
return memoResponseDto;
}
public List<MemoResponseDto> getMemos() {
// DB 조회
MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
return memoRepository.findAll();
}
public Long updateMemo(Long id, MemoRequestDto requestDto) {
MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
// 해당 메모가 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) {
MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
// 해당 메모가 DB에 존재하는지 확인
Memo memo = memoRepository.findById(id);
if (memo != null) {
// 메모 삭제
memoRepository.delete(id);
return id;
} else {
throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
}
}
}
src > main > java > com.sparta.memo > repository > MemoRepository
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 java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
public class MemoRepository {
private final JdbcTemplate jdbcTemplate;
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 deleteSql = "DELETE FROM memo WHERE id = ?";
jdbcTemplate.update(deleteSql, 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);
}
}
3-3 IoC와 DI
IoC(제어의 역전) : 설계 원칙 > 준비해야 할 것, 꼭 해야하는 것
DI(의존성 주입) : 디자인 패턴 > 내부 인테리어. 레시피
강하게 결합된 클래스를 나누는데 유용하다.
- Intrface의 다형성을 사용
- 필드에 주입 Fild fild1;
Field field1;
- 메서드에 주입
Field fild1;
Public void setField(Field field1){
this.field1=field1);
}
- 생성자에 주입
public class Fuction {
Field field1;
public Fuction(Field field1){
this.field1=field1;
}
}
3-4 메모장 프로젝트에 IoC & DI
1. 반복코드 줄이기
MemoService에 반복되는 코드가 있다.
MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
이를 모두 삭제하고 하나로 줄여보자.
맨앞을 이거로 바꿔주면 된다.
private final MemoRepository memoRepository; //MemoRepository가져오기
public MemoService(JdbcTemplate jdbcTemplate) {
this.memoRepository = new MemoRepository(jdbcTemplate); //설정해주기
}
MemoController에도 반복됨으로 바꾸어 준다.
private final MemoService memoService;
public MemoController(JdbcTemplate jdbcTemplate) {
this.memoService = new MemoService(jdbcTemplate);
}
2. 강한 결합을 느슨한 결합으로 변경
하지만 MemoController > MemoService > MemoRpository처럼 강한 결합의 문제점은
모든 Contoller 와 모든 Service의 코드 변경이 필요하다.
따라서 해결법은
1. 각 객체에 대한 객체 생성은 딱 1번만!
2. 생성된 객체를 모든 곳에서 재사용!
3. 생성자 주입을 사용하여 필요로 하는 객체에 해당 객체 주입!
MemoController.java
private final MemoService memoService;
public MemoController(MemoService memoService) {
this.memoService = memoService;
}
MemoService.java
private final MemoRepository memoRepository;
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
3. bean 오류는 무엇일까?
Bean : Spring이 관리하고 있는 객체
IoC 컨테이너: Bean(Spring이 관리하고 있는 객체)이 모여져 있는 컨테이너
@Component 달아주기
- MemoApplication.java
@SpringBootApplication 에 같은 위치와 아래에 있는 @Component를 찾아내어 객체로 만든다.
@ SpringBootApplication를 Ctrl+클릭하면 볼 수 있음.
- Bean으로 저장하고 있기 때문에 Repository에 jdbcTemplate를 불러 올 수 있었다.
- 만약 생성자 하나로 객체를 불러오는게 아니면 자동으로 Bean에 받아 올 수 없다.
이럴땐 @Autowired를 앞에 붙여준다.
Method로 주입하는 법
private MemoRepository memoRepository;
@Autowired
public void setDi(MemoRepository memoRepository){
this.memoRepository = memoRepository;
}
Field로 주입하는 법 (이것만 붙이면 되지만 추천하지 않음)
@Autowired
private MemoRepository memoRepository;
★ 개체 불변성을 지켜주기 때문에 생성자로 주입하는 법을 추천! (생성자가 하나면 @Autowired 생략 가능)
private final MemoRepository memoRepository;
//@Autowired
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
4. Lombok을 사용하는 법
@RequiredArgsConstructor 추가하기
final로 클래스 받기
5. IoC컨테이너에 직접 접근해서 주입하는 수동적인 방법
import org.springframework.context.ApplicationContext; //추가해주기
@Component
public class MemoService {
private final MemoRepository memoRepository;
public MemoService(ApplicationContext context) {
// 1.'Bean' 이름으로 가져오기
MemoRepository memoRepository = (MemoRepository) context.getBean("memoRepository");
// 2.'Bean' 클래스 형식으로 가져오기
//MemoRepository memoRepository = context.getBean(MemoRepository.class);
this.memoRepository = memoRepository;
}
6. Component를 Service와 Repository로 바꿀 수 있다.
'웹공부 > SPRING' 카테고리의 다른 글
SPRING 기본 (1) | 2024.12.13 |
---|---|
Intellj Community Edition에서 SPRING 작동하는 법 (0) | 2024.12.13 |
스파르타 코딩 클럽 - Spring 4주차 (0) | 2024.07.26 |
스파르타 코딩 클럽 - Spring 2주차 (2) | 2024.07.24 |
스파르타 코딩 클럽 - Spring 1주차 (0) | 2024.07.17 |