✏️ Layered Architecture
컨트롤러의 책임이 너무 많아서 3가지 계층으로 나눈다.
구조
- Presentation Layer
- 사용자의 요청을 받고 응답하는 역할을 수행한다.
- 화면을 응답하거나 데이터를 응답하는 API를 정의한다.
- Business Layer(Service Layer)
- 비지니스 로직을 수행한다.
- 요청을 해석하여 Repository Layer에 전달한다.
- 일반적으로 하나의 비지니스 로직은 하나의 트랜잭션으로 동작한다.
- Data Access Layer(Repository Layer)
- 데이터베이스와 연동되어 실제 데이터를 관리한다.
👉🏻 결국은 그냥 controller, service, repository로 나눈다는 것
- 용어 설명
- DTO(Data Transfer Object)
- 계층간 데이터 전달을 위해 사용되는 객체이다.
- 요청 데이터를 처리하는 객체는 일반적으로 RequestDto로 명명한다.
- 응답 데이터를 처리하는 객체는 일반적으로 ResponseDto로 명명한다.
- Model
- Entity
- 추후 숙련주차에 배울 JPA와 관련이 있다.
- JPA에서는 Entity라는 형태로 데이터를 반환한다.
- Entity
- DAO(Data Access Object)
- DTO(Data Transfer Object)
✏️ Database
DBMS
데이터베이스를 관리하고 운영하는 시스템 어플리케이션에서 DBMS에 데이터를 요청하면 DBMS에서 관리하는 DB에서 데이터를 가져와 응답한다.
- DBMS의 종류
- 관계형 DMBS(RDBMS) 중요!
- 가장 많이 사용하는 데이터베이스
- 데이터를 테이블 형태로 구조화하여 저장하고 관리하는 시스템이다.
- 테이블 간의 관계를 이용해 데이터를 연결한다.
- 비관계형 DBMS(NoSQL)
- 테이블이 아닌 key-value, document, graph 등의 다양한 형태로 데이터를 저장하고 관리한다.
- 스키마가 고정되지 않고, 대규모 데이터 처리와 높은 확장성을 제공한다.
- 다중 모델 DBMS
- 하나의 데이터베이스 관리 시스템에서 여러 데이터 모델을 지원하는 시스템이다.
- 동일한 DBMS에서 관계형 데이터뿐만 아니라 문서형, 그래프형 데이터를 함께 관리할 수 있다.
- 관계형 DMBS(RDBMS) 중요!
트랜잭션
DB에서 하나의 논리적인 작업단위. 트랜잭션으로 묶여있는 작업들은 모두 성공적으로 완료되거나 하나라도 실패하면 전체가 취소된다.
sql 종류
- DDL(Data Definition Language)
- 데이터베이스 구조를 정의하는 데 사용된다.
- CREATE
- 새로운 데이터베이스 및 테이블을 생성한다.
- ALTER
- 기존 데이터베이스 및 테이블 구조를 수정한다.
- DROP
- 데이터베이스 및 테이블을 삭제한다.
- DML(Data Manipulation Language)
- 데이터베이스의 데이터를 조작하는 데 사용된다.
- INSERT
- 데이터를 테이블에 삽입한다.
- UPDATE
- 테이블의 기존 데이터를 수정한다.
- DELETE
- 테이블의 데이터를 삭제한다.
- DQL(Data Query Language)
- 데이터베이스에서 데이터를 검색하는 데 사용된다.
- SELECT
- 데이터를 조회한다. 특정 조건을 추가할 수 있다.
- DCL(Data Control Language)
- 데이터베이스의 권한을 관리하는 데 사용된다.
- GRANT
- 사용자에게 권한을 부여한다.
- REVOKE
- 사용자의 권한을 회수한다.
- TCL(Transaction Control Language)
- 여러 DML 작업을 하나의 논리적 단위로 묶어 트랜잭션으로 처리하는 데 사용된다.
- COMMIT
- 트랜잭션이 성공한 것을 데이터베이스에 알리고 모든 변경 사항을 영구적으로 저장한다.
- ROLLBACK
- 트랜잭션 중 발생한 모든 변경 사항을 취소하고, 데이터베이스를 트랜잭션 시작 시점의 상태로 되돌린다.
JDBC의 주요 특징
- 표준 API
- 대부분의 RDBMS (관계형 데이터베이스 관리 시스템)에 대한 드라이버가 제공되어 여러 종류의 DB 대해 일관된 방식으로 상호 작용할 수 있다.
- 데이터베이스 연결
- SQL 쿼리 실행
- Prepared Statement
- 결과 집합 처리(Result Set)
- 데이터베이스로부터 반환된 결과 집합을 처리할 수 있다.
- 트랜잭션 관리
- JDBC를 사용하여 데이터베이스 트랜잭션을 시작, 커밋(성공) 또는 롤백(실패)하는 등의 트랜잭션 관리 작업을 수행할 수 있습니다.
Mybatis
- SQL 쿼리들을 XML 파일에 작성하여 코드와 SQL을 분리하여 관리되도록 만들어준다.
- SQL과 Java Code의 분리가 핵심
- Query를 JAVA에서 XML로
- 복잡한 JDBC 코드가 사라진다.
- ResultSet과 같이 결과값을 맵핑하는 객체가 없다.
- 설정이 간단하다.
- 관심사를 분리한다. → SQL 을 따로 관리한다.
- XML 안에있는 SQL을 Java의 메소드에 매핑해준다.
- MyBatis 장점
- 자동으로 Connection 관리를 해주면서 JDBC 사용할 때의 중복 작업 대부분을 없애준다.
- DB 결과 집합을 자바 객체로 매핑할 수 있다.
- 복잡한 쿼리나 다이나믹하게(동적쿼리) 변경되는 쿼리 작성이 쉽다.
- 상황에 따라 분기처리(IF)를 통해 쿼리를 동적으로 만들어주는것.
- 관심사 분리 - DAO로부터 SQL문을 분리하여 코드의 간결성 및 유지보수성이 향상된다.
- 쿼리 결과를 캐싱하여 성능을 향상시킬 수 있다.
// User 클래스
public class User {
private Long id;
private String userName;
private String email;
// Getter and Setter methods
}
// Mapper Interface 생성 : SQL 쿼리와 매핑을 정의하는 인터페이스
public interface UserMapper {
User getUserById(Long id);
}
public class Main {
public static void main(String[] args) {
String resource = "mybatis-config.xml";
try (Reader reader = Resources.getResourceAsReader(resource)) {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.getUserById(1L);
System.out.println(user.getId() + ", " + user.getUsername() + ", " + user.getEmail);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
<mapper namespace="com.example.UserMapper">
<select id="getUserById" resultType="com.example.User">
SELECT id, userName, email
FROM users
WHERE id = #{id}
</select>
</mapper>
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mydb" />
<property name="userName" value="userName" />
<property name="password" value="password" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/example/UserMapper.xml" />
</mappers>
</configuration>
✏️ 마무리
이것만은 꼭 기억하기
- Spring MVC 구조
- 프론트 컨트롤러 : DispatcherServlet
- 어댑터 패턴 : HandlerAdapter
- View 응답 : View Resolver
- 중요! Client to Server 데이터 전송하는 방법 3가지
- GET + Query Parameter(=Query String)
- POST + HTML Form(x-www-form-urlencoded)
- HTTP Request Body
- 중요! Server to Client 데이터 응답하는 방법 3가지
- 정적 리소스
- View Template
- HTTP Response Body
- Spring Annotation
- Controller
- @Controller(View 응답), @RestController(데이터 응답)
- @RequestMapping(Post, Get, Put, Patch, Delete)
- @PathVariable, @RequestParam, @MoelAttribute, @RequestBody 요청
- @ResponseBody, HttpResponseEntity<> 응답
- 각 어노테이션의 사용법 및 주의사항
- HttpMessageConverter
- Controller
- Layered Architecture
- Controller의 역할을 분담한다.
- Controller
- 요청, 응답
- Service
- 비지니스 로직
- @Service
- Repository
- 데이터베이스 상호 작용
- @Repository
- DTO
- Layer간 데이터 전달
- Prepared Statement
- query를 미리 준비한다.
- Statement에 비해 성능면에서 뛰어나다.
- 웹 보안을 유지할 수 있다.
- Persistence Framework
- JDBC API를 사용한다.
- Prepared Statement를 사용한다.
- JDBC 처럼 복잡함이나 번거로움 없이 간단한 작업만으로 Database와 연동된다.
- 자원 관리(Connection)를 자동으로 해준다.
- SQL Mapper
- JDBC Template
실습 요약
- 실습을 기반으로 모든 CRUD API를 만들 수 있다.
- 우리가 실습으로 만들어낸건 메모 CRUD API로 대부분의 구조가 정형화 되어있다.
- 그렇다면 우리가 할일은? 메모 → 다른 무엇인가로 바꾸면된다.
- 아직은 문제점들을 가지고 있다.
- 예외 발생시 공통적으로 처리가 불가능하다.
- 각각의 모든 예외를 try-catch 하여 처리해야 한다.
- 예시코드
- 예외 발생시 공통적으로 처리가 불가능하다.
@Slf4j
@RestController // @Controller + @ResponseBody
@RequestMapping("/memos") // Prefix
public class MemoController {
// 주입된 의존성을 변경할 수 없어 객체의 상태를 안전하게 유지할 수 있다.
private final MemoService memoService;
/**
* 생성자 주입
* 클래스가 필요로 하는 의존성을 생성자를 통해 전달하는 방식
* @param memoService @Service로 등록된 MemoService 구현체인 Impl
*/
private MemoController(MemoService memoService) {
this.memoService = memoService;
}
/**
* 메모 삭제 API
* @param id 식별자
* @return {@link ResponseEntity<Void>} 성공시 Data 없이 200OK 상태코드만 응답.
* @exception ResponseStatusException 식별자로 조회된 Memo가 없는 경우 404 Not Found
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteMemo(@PathVariable Long id) {
try {
memoService.deleteMemo(id);
} catch (ResponseStatusException e) {
log.error(e.getReason());
}
// 성공한 경우
return new ResponseEntity<>(HttpStatus.OK);
}
}
2. RequestDto, ResponseDto를 공유하여 null값이 들어오기도 한다.
3.필요없는 필드에 추가적인 null 검사를 해야한다.
'코딩 > 자바' 카테고리의 다른 글
[Spring] SOLID 원칙 (0) | 2025.02.04 |
---|---|
[Spring] Spring 3 Layered, Spring MVC (1) | 2025.01.23 |
[Spring] HTTP API 설계 기본 (0) | 2025.01.23 |
[Spring] 기초 5주차 (0) | 2025.01.23 |
[Spring] 기초 4주차 (1) | 2025.01.22 |