[Architecture] Layered Architecture
๐ก ๋ ์ด์ด๋ ์ํคํ ์ณ๋?
๋ ์ด์ด๋ ์ํคํ
์ณ๋ ์ํํธ์จ์ด๋ฅผ ์ญํ ๋ณ๋ก ์์ง ๊ณ์ธต(layer) ์ผ๋ก ๋๋๋ ์ค๊ณ ๋ฐฉ์์ด๋ค.
๊ฐ ๊ณ์ธต์ ์์ ์ ์ฑ
์๋ง ์ํํ๋ฉฐ, ํ์ ๊ณ์ธต์๋ง ์์กดํ๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ค.
์ด ๊ตฌ์กฐ๋ ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ(Separation of Concerns)๋ฅผ ํตํด
์ฝ๋์ ๊ฐ๋
์ฑ, ์ ์ง๋ณด์์ฑ, ์ฌ์ฌ์ฉ์ฑ์ ํฅ์์ํค๋ ๋ฐ ๋ชฉ์ ์ด ์๋ค.
๐ ์ฃผ์ ๋ ์ด์ด ๊ตฌ์ฑ (4๊ณ์ธต)
์ ๋ฐฉํฅ์ผ๋ก๋ง ์์กดํ๋๋ก ์ค๊ณ
์๋ฅผ ๋ค์ด, Repository๊ฐ Service ํธ์ถโ
๊ณ์ธต๋ณ ์ค๋ช
- Presentation Layer (Controller)
- ์น ์์ฒญ ์์ , ์์ฒญ ํ๋ฆ ์ ์ด
- ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋ฐ๊ณ ์๋ต์ ๋ฐํํ๋ ์ญํ (Request, Response)
- Business Layer (Service)
- ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง ์ํ
- Persistence Layer (Repository)
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ง์ ํต์ ํ๋ ๊ณ์ธต (JPA, MyBatis ๋ฑ)
- Database Layer (DB)
- DB ํ ์ด๋ธ๊ณผ ๋งคํ๋๋ ํต์ฌ ๋๋ฉ์ธ ๊ฐ์ฒด
DTO (Data Transfer Object)
- Controller โ Service ๋๋ Service โ ์ธ๋ถ API ๊ฐ ๋ฐ์ดํฐ ์ ๋ฌ์ฉ
- Entity๋ฅผ ์ง์ ๋ ธ์ถํ์ง ์๊ธฐ ์ํด ์ฌ์ฉ
๐ป ๋์ ๊ด๋ฆฌ ์์คํ ์์
API ํ๋ฆ
1
2
3
4
5
6
7
8
9
[ํด๋ผ์ด์ธํธ ์์ฒญ]
โ์์ฒญ โ์๋ต
Controller (BookController)
โํธ์ถ โ๊ฒฐ๊ณผ
Service (BookService)
โํธ์ถ โ๊ฒฐ๊ณผ
Repository (BookRepository)
โ์กฐํ โ๊ฒฐ๊ณผ
Entity (Book) โ DB Table (books)
์ฝ๋(์กฐํ ๊ธฐ๋ฅ) ์์
controller/BookController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) {
this.bookService = bookService;
}
// 1. ํด๋ผ์ด์ธํธ๊ฐ "/books/{id}" ๋ก Get ์์ฒญ
// ?keyword=
@GetMapping
public ResponseEntity<List<BookDto>> searchBooks(
@RequestParam(required = false) String keyword;
) {
// ํด๋ผ์ด์ธํธ์๊ฒ ์๋ต ์ Dto ๋ก ๋งคํ
List<BookDto> bookDtoList;
// 2. Service ์กฐํ ๊ธฐ๋ฅ ํธ์ถ
if (keyword != null && !keyword.trim().isEmpty()) {
bookDtoList = bookService.findByKeyword(keyword);
} else {
bookDtoList = bookService.findAll();
}
// ์๋ต
return ResponseEntity.ok(bookDtoList);
}
}
service/BookService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@service
public class BookService {
private final BookRepository bookRepository;
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
// 3. ์ค์ DB ์กฐํ๋ Repository ์ ์์(ํธ์ถ)
public List<BookDto> findAll() {
// ๊ฒฐ๊ณผ ๋ฐ์ดํฐ๋ฅผ DTO ๋ก ๋งคํ ํ Controller ์๊ฒ ๋ฐํ
return bookRepository.findAll()
.stream()
.map(BookDto::new)
.toList();
}
public List<BookDto> findByKeyword(String keyword) {
return bookRepository.findByTitleContainingIgnoreCase(keyword)
.stream()
.map(BookDto::new)
.toList();
}
}
repository/BookRepository.java
1
2
3
4
5
// 4. DB ์กฐํ
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findByTitleContainingIgnoreCase(String keyword);
}
entity/Book.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Getter
@Entity
@Table(name = "books")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
private String isbn;
private String category;
private LocalDateTime createdAt;
public Book() {}
public Book(String title, String author, String isbn, String category) {
this.title = title;
this.author = author;
this.isbn = isbn;
this.category = category;
this.createdAt = LocalDateTime.now();
}
}
dto/BookDto.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Getter
public class BookDto {
private Long id;
private String title;
private String author;
private String isbn;
private String category;
private String createdAt;
public BookDto(Book book) {
this.id = book.getId();
this.title = book.getTitle();
this.author = book.getAuthor();
this.isbn = book.getIsbn();
this.category = book.getCategory();
this.createdAt = book.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
}
}
โ ์ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํด์ผํ ๊น?
Presentation Layer(Controller) ์์ ์ง์ DB ์ ์ ๊ทผํ๋ ๊ฒ์ด ํจ์ฌ ๋น ๋ฅด๊ณ ์ง๊ด์ ์ผํ
๋ฐ,
์ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํด์ผํ ๊น?
1๏ธโฃ ์ ์ง๋ณด์๊ฐ ์ฌ์์ง
๊ธฐ๋ฅ๋ณ๋ก ์ฝ๋๊ฐ ๋ถ๋ฆฌ๋์ด ์๊ธฐ ๋๋ฌธ์,
์ด๋ ๋ถ๋ถ์ ์์ ํด์ผ ํ๋์ง ๋ช
ํํ๊ฒ ํ์
ํ ์ ์์ด
๋๋ฒ๊น
๊ณผ ์ ์ง๋ณด์๊ฐ ํธํด์ง๋ค.
2๏ธโฃ ๋ณ๊ฒฝ์ ์ ์ฐํจ
์๋ฅผ ๋ค์ด
- ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ
MySQLโMongoDB๋ก ๋ฐ๊ฟ ๋Repository๋ง ์์ - ์๋ต ํฌ๋งท์
JSONโXML๋ก ๋ฐ๊ฟ ๋Controller๋ง ์์
์ฆ, ํ ๋ถ๋ถ์ ๋ฐ๊ฟ๋ ๋ค๋ฅธ ๋ถ๋ถ์ ์ํฅ์ด ์ ๋ค.
3๏ธโฃ ํ ์คํธ๊ฐ ์ฌ์์ง
- ๊ฐ ๊ณ์ธต์ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํ ์ ์์ด์ ๋จ์ ํ ์คํธ ์์ฑ์ด ์ฉ์ด
DB์์ด๋Service๊ณ์ธต์ ํ ์คํธํ ์ ์๋ค.
4๏ธโฃ ํ์ ์ด ํจ์จ์
- ์ญํ ์ด ๋ถ๋ฆฌ๋์ด ์๊ณ , ๊ตฌ์กฐ๊ฐ ๋ช
ํํ๊ธฐ ๋๋ฌธ์ ํ์
์ ์ ๋ฆฌ
- A๊ฐ๋ฐ์:
Controller - B๊ฐ๋ฐ์:
Repository - C๊ฐ๋ฐ์:
Service
- A๊ฐ๋ฐ์:
5๏ธโฃ ๊ณตํต ๊ธฐ๋ฅ ์ฒ๋ฆฌ์ ์ ์ฐ
๋ก๊น , ๋ณด์, ํธ๋์ญ์ ๋ฑ
6๏ธโฃ ์ฌ์ฌ์ฉ์ฑ ์ฆ๊ฐ
Service, Repository ๋ฑ์ ๋ก์ง์ ๋ค๋ฅธ ๊ณณ์์ ์ฌํ์ฉ์ด ๊ฐ๋ฅํ๋ค.
๐ ์ ๋ฆฌ
์ด์ฒ๋ผ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ๋ฉด ์ฝ๋๊ฐ ๋ ๋ช
ํํด์ง๊ณ , ๊น๋ํ๊ณ , ํ
์คํธํ๊ธฐ ์ฌ์ด ๊ตฌ์กฐ๊ฐ ๋๋ค.
๊ด์ฌ์ฌ ๋ถ๋ฆฌ๋ ๋จ์ํ ์ค๊ณ ๊ท์น์ด ์๋, ์ ์ง๋ณด์์ฑ๊ณผ ํ์ฅ์ฑ์ ๋์ด๋ ํต์ฌ ์์น์ด๋ค.
โ ๏ธ ๋จ์
- ๊ณ์ธต ๊ฐ ๊ฒฝ๊ณ ์ฝํ ์ํ
- ํ ๋ ์ด์ด๊ฐ ๋๋ฌด ๋ง์ ์ฑ
์์ ์ง ๊ฒฝ์ฐ ๋น๋ํด์ง ์ ์๋ค. (ํนํ
Service)
- ํ ๋ ์ด์ด๊ฐ ๋๋ฌด ๋ง์ ์ฑ
์์ ์ง ๊ฒฝ์ฐ ๋น๋ํด์ง ์ ์๋ค. (ํนํ
- ๋จ๋ฐฉํฅ ๊ตฌ์กฐ์ ํ๊ณ
- ์ง๋์น๊ฒ ๊ณ์ธตํ๋๋ฉด ๋ณต์กํ ๋ก์ง ํ๋ฆ์ด ์คํ๋ ค ๋ถํธํด์ง ์ ์๋ค.
- ๋๋ฉ์ธ ์ค์ฌ์ด ์๋
- ๋น์ฆ๋์ค ๋๋ฉ์ธ์ ์ค์ฌ์ผ๋ก ์ค๊ณํ๋ DDD, ํด๋ฆฐ ์ํคํ ์ณ๋ณด๋ค ์ ์ฐ์ฑ์ด ๋ฎ๋ค.
