라우터(디스패치) / 컨트롤러

Jan 30, 2024
라우터(디스패치) / 컨트롤러

들어가기 전

notion image
DAO에 update가 들어오면 update를 호출할 것. 자동으로 되면 좋겠지만... 우리는 url = "/insert"; 이렇게 수기로 넣어줄거다. 지금은 라우터랑 컨트롤러의 개념만 할 것 이니까…
 

BankController (Controller 만들기)

notion image
BankController를 만든다. 애가 모든 요청을 다 받을 것이다. 엄청 중요하다
notion image
일단, BankController에 필요한 메소드를 (구분만 되는 정도로) 다 적어놓는다.
💡
컨트롤러는 바디 데이터를 파싱 하겠지… 그리고 유효성 검사도 하고… DAO도 찾아줌…
💡
컨트롤러는 디스패처(Dispatcher)로부터 전달받은 요청을 분석하고, 그에 맞는 비즈니스 로직을 실행하거나 다른 서비스와의 상호작용을 조정한다.
 

라우터 = 디스패처

어떤 주소가 들어왔을 때, 그걸 보고 라우팅을 한다. 그래서 라우터라고 부른다 근데 디스패쳐라고 부르는 사람도 있다. 막!! 추적해서 찾아주는 것! (url을 받으면 /insert 뭐 이렇게 파싱해서 적절한 컨트롤러를 찾아준다.)
💡
디스패처는 라우팅만 함 .바디 데이터 파싱 안함
notion image
💡
이게 바로 라우터(디스패쳐)다. 받아서 전달해주는 것!
a데이터 들어오면 일로가. b데이터 들어오면 일로가. 이러는게 라우터. 네트워크에서 데이터를 받아서 전달해주는 역할?
notion image
notion image
그러니까, 외부에서 'insert' 가 들어오니 con.insert 값을 출력한다 그런데 지금... 라우터(디스패처)를 BankApp(메인)에 작성했으니, 옮겨주자
 

Dispatcher 작성 + 의존성 주입 설명

💡
이제 디스패쳐를 만들자. 이 디스패쳐에 방금 작성한 라우터 코드들을 옮겨준다 왜냐하면 메인에는 new만 띄워주려고 (main 클래스를 깔끔하게 유지하기 위해서)
notion image
notion image
이렇게 작성해줬다. 그런데... 커피 자판기가 커피를 만드는 책임을 가지고 있지, 고객이 커피를 만드는 책임을 가지고 있진 않잖아. 단지 원하는 커피를 선택하고 버튼을 누르는 역할을 할 뿐... 때문에 누구도 만들 책임이 없는건 메인에서 만든다. (=의존성 주입. 의존하고 생성자로 주입해주겠지.) 즉, BankController con = new BankController(); 이 코드는 main에 있어야 한다.

[ 의존성 주입 ] 은 생성자로 주입

notion image
Dispatcher가 Controller를 만들지 않고, Controller가 DAO를 만들지 않잖아. main에서 다다다다 만들어 놓고 실행하는 거잖음. 그러니 '누구도 만들 책임이 없으니 메인에서' 만든다.
 

 
notion image
BankApp... 즉, main에 만들어줬다.
notion image
Dispatcher는 Controller와 의존 관계이기 때문에, Dispatcher 안에 BankController를 필드로 받고, 생성자로 의존관계를 주입해줬다.
notion image
실행되는 것 확인! 근데 저 생성자는 롬복으로 … 만들자
notion image
 

디스패처는 컨트롤러에 의존한다

notion image
Dispatcher와 BankController는 의존 관계다. 그러니 생성자에 주입할 것이다 (의존적인 관계는 무조건 생성자로 넘겨줌!!) (의존하는 객체를 다른 객체의 생성자 파라미터로 전달하여 의존성을 주입하는 것을 의미함 생성자를 통해 의존성을 주입받은 객체는 의존하는 객체의 메서드나 속성을 사용할 수 있다.) 즉... 다른 클래스의 인스턴스를 클래스의 필드로 가진다! (디스패처는 컨트롤러의 메서드를 사용해서 작동하기에) 요청 들어올 때마다 new할 필요 없이 한 번만 메모리에 뜨면 되잖아. 그래서 메인에 한 번만 만들어서 의존시키면 된다 메인 함수에서 한 번만 객체를 생성하고 필요한 곳에서 의존성 주입을 통해 사용!!
웹 애플리케이션에서 사용자가 특정 URL로 접속하면 그 요청을 디스패처가 받아서 어떤 컨트롤러에게 처리를 맡길지 결정한다. 이때 디스패처는 컨트롤러의 존재를 알고 있어야 한다. 따라서 디스패처는 컨트롤러에 의존하게 되는 것! 디스패처는 컨트롤러에게 요청을 전달하고, 컨트롤러는 해당 요청을 처리하고 결과를 다시 디스패처에게 돌려준다. 이런식으로 디스패처와 컨트롤러는 서로 의존하면서 협력하게 된다. 디스패처는 컨트롤러의 도움을 받아 요청을 처리하고, 컨트롤러는 디스패처로부터 요청을 받아 실제 작업을 수행한다.
notion image
notion image
 

[ Dispatcher와 Controller의 책임은? ]

클래스를 만들면 무조건 srp(책임을 최대한 적게 가짐)을 해야한다. 왜냐, 오류가 났을때 책임을 나눠야지만 오류 발견을 쉽게 발견할 수 있기 때문! Dispatcher의 책임은 : 라우팅이다! > 애는 단순히 컨트롤러만 찾아주는 책임 Controller의 책임은 : 유효성 검사(바디데이터), 파싱(바디데이터), 적절한 DAO 찾기 바디 데이터는 파싱 안하면 의미없다. 파싱이 끝나면 이 바디 데이터가 적절한지 유효성 검사를 하는 것. 패스워드가 안들어왔네? 밸런스가 안들어왔네? 이런걸 검증하는 것!! 그 검증이 끝나야지 DAO를 호출할 수 있음! 즉, Controller는 책임이 3개다.
💡
유효성 검사를 통과한 바디 데이터를 기반으로 BankController는 적절한 DAO를 찾아 호출한다. DAO는 데이터베이스나 외부 서비스와의 상호작용을 담당하는 객체로, BankController는 요청에 대응하는 DAO를 선택하여 데이터의 저장, 조회, 수정, 삭제 등의 작업을 수행
 

BankApp 전달

notion image
마찬가지로 이렇게 매번 new 할 순 없으니, main에다 BankDAO 객체를 만들어줄 것임 위에서 의존성 주입 설명했죠? 메인에 전부 new를 띄워놓고, 의존성이 있는 필요한 곳에 생성자로 전달만 해주는 것!! (메인 시작할 때 한 번만 딱 띄우는 것임)
notion image
생성자로 주입(의존). DAO랑 Controller 도 의존 관계 컨트롤러가 DAO에 의존한다. 컨트롤러가 DAO의 메소드를 사용한다 컨트롤러는 DAO를 호출하여 데이터베이스에서 데이터를 읽거나 쓰는 등의 작업을 수행한다. 따라서 컨트롤러는 DAO에 의존하여 데이터 액세스를 처리하며, DAO의 메소드를 호출하여 필요한 데이터를 가져오거나 업데이트한다.
notion image
이렇게!! Controller에서 BankDAO를 필드 값으로 받아서 사용! (안 그러면 수영장 튜브를 계속 불고 빼고 불고 빼고 함.)
 

순서도 및 설명

notion image
이런 (의존) 관계다. (추후 메인은 통신이 되어야하니까 클라이언트에게 요청을 받음!)
클라이언트한테 (ex.브라우저) 요청을 받고, 디스패처가 적합한 컨트롤러에 라우팅하여 데이터를 던진다. 그럼 컨트롤러가 파싱 및 유효성 검사를 하고, 통과하면 DAO한테 다시 넘겨줌. 그럼 DAO가 DB와 상호작용(CRUD 작업) 함. 이후 DAO로부터 받은 결과를 컨트롤러가 파싱하고 디스패처한테 주고 클라이언트한테 전해주는 것. (클라이언트한테 갈 때도 물론 파싱을 하겠지)

[ 순서도 요약 ]

디스패처가 데이터를 받고, 적합한 컨트롤러에 라우팅 함 > 컨트롤러는 바디 데이터 파싱 및 유효성 검사를 한 후 > DAO한테 데이터를 주면, DAO가 DB와 상호작용을 함. 이후 얻은 결과를 컨트롤러한테 넘김 > 컨트롤러는 DAO에게 받은 결과를 디스패처한테 넘김 > 디스패처는 컨트롤러에게 받은 결과를 클라이언트한테 넘겨줌
notion image
 

전체 코드

import controller.BankController; import dao.BankDAO; public class BankApp { public static void main(String[] args) { String url = "selectAll"; BankDAO dao = new BankDAO(); BankController con = new BankController(dao); Dispatcher dis = new Dispatcher(con); dis.route(url); } }
import controller.BankController; import lombok.AllArgsConstructor; //책임 : 라우팅, 적절한 컨트롤러 찾기 @AllArgsConstructor public class Dispatcher { //컴포지션 private BankController con; public void route(String url) { //라우터, 디스패쳐 if (url.equals("insert")) { con.insert(); } else if (url.equals("delete")) { con.delete(); } else if (url.equals("update")) { con.update(); } else if (url.equals("selectOne")) { con.selectOne(); } else if (url.equals("selectAll")) { con.selectAll(); } } }
package controller; // 책임 : 유효성 검사(바디데이터), 파싱(바디데이터), 적절한 DAO 찾기 import dao.BankDAO; import lombok.AllArgsConstructor; @AllArgsConstructor public class BankController { private BankDAO dao; public void insert() { // 1. 파싱 (split 그거.. 짤라내는 것) // 2. 유효성 검사 (비번을 4자만 받아야하는데 5천자 받을 수도 있잖아) // 3. DAO 찾기 System.out.println("controller : insert"); dao.insert("1234", 1000); } public void delete() { System.out.println("controller : delete"); dao.deleteByNumber(1); } public void update() { System.out.println("controller : update"); dao.updateByNumber(1000,1); } public void selectOne() { System.out.println("controller : selectOne"); dao.selectByNumber(1); } public void selectAll() { System.out.println("controller : selectAll"); dao.selectAll(); } }
 

TIP!

💡
유효성 검사는 프론트에서도 하고 , 백엔드에서도 하고... 둘 다 해야 함. 같이 막아야 함!
💡
디스패처를 보면, 주소(URL) 같은게 들어오면 파싱하는게 어렵다. 예시처럼 깔끔하게 “insert” 이렇게 안들어온다. 일단 주소 URL만 몇 백개고…. url 디스패처 하는게 쉬운 일이 아니다 ㅠㅠ 이걸 매번 한다? 노가다다.
https://section.cafe.naver.com/ca-fe/home 막 이런 주소가 어마어마하게 많은데 이 주소를... 개발자가 다 만들어야한다 라우터를 라이브러리로 만들어준다는게 엄청나게 어렵다 근데 무려... 스프링이 이걸 만들어준다. 스프링이 라우터를 만들어준다!
 
Share article

codingb