아파치ㆍ톰캣 흐름도

Jan 31, 2024
아파치ㆍ톰캣 흐름도

[ 흐름도 달달 외우기 ] ★ - 왐마야

notion image
notion image
💡
디스패처 서블릿은 프런트 컨트롤러의 한 종류
 

1. 아파치

notion image
서버소켓 8080 / 클라이언트와 연결된 소켓 5000 이라고 치자. 먼저, web.xml은 문지기가 해야할 일이 적혀있고, 문지기는 이 web.xml 파일을 보고 일한다. (문지기에게 명령을 내릴때 web.xml에다 적는다) 문지기는 특정 IP 주소를 차단하는 등 데이터의 가공이나 변환 작업을 수행할 수 있어서 '필터'라고도 불린다. (stream에서 filter와 같은 기능을 함.) 이 문지기가 딱 보고 있다가 이상한 건(?) 팅구는 것. (막,, GET요청 들어오면 다 쫓아내!! 이러는 일을 문지기가 한다는...) 이 문지기를 통과해서 처음으로 마주치는게 바로 아파치다 아파치가 index.html 찾아서 내놔야하는데... 전에도 말했듯이 요청을 받았을 때, 드라이브 전체 권한을 줄 수는 없으니까 webapp 폴더에 접근을 제한한다. 아파치가, webapp에 있는 파일을 읽어서, 클라이언트에게 보낸다. 파일을 찾아주고, 버퍼드 리더로 클라이언트에게 보내는 것. 이게 바로 아파치의 역할
💡
문지기(web.xml) : 이 아이피 맘에 안들어! 팅굴 것이다!! * 난 b아파트 주민. 그치만 a아파트에 가고 싶지 > 경비가 web.xml을 보고 아파트 앞에서 바로 팅굼
* 나는 a아파트 주민. 그럼 난 주민이니까 경비는 쫓아내지 못함. 근데 내 집이 아닌 집을 두드리면 쫓아내겠죠… 이렇게 더 안에서 튕구는 경우가 있다
누가 8080을 때리면 > 소켓 생성 > 주소 요청이 들어간다. (GET 요청이니 헤더 요청만 있음) (요청을 한단 말은 bw에 뭔가를 써서 보낸단 말이고, 소켓은 br로 읽는다.) http://localhost:8080/ff.html 뭐 이렇게 들어가면서 IP주소와 디바이스 정보... 등등 수많은 정보들이 프로토콜에 쌓여서 BW에 담겨 같이 날아간다. 바이트 스트림을 타고 내려가니 전부 문자열로 받아오는데, 문자열로 받으면 아파치는 이해하기 어렵다. 때문에 그 수 많은 문자열이 뒤섞여있는걸 파싱해서 아파치가 자기 객체로 만든다. (복잡한 데이터를 클래스에 넣어주는데... 아파치가 없으면 내가 해야함..) 그래야만 /index.html을 이해할 수 있으니까… 요청받고 하려면 필터도 분석을 해야하니까... 필터 전에 파싱을 해야함. 그러니 아파치의 크기는 사실...
notion image
이만하다! (파란 박스가 다 아파치 영역)
💡
webapp은 내가 만든게 아니고 프로그램 까니까 자동으로 설정된 것 이거 우리가 나중에 바꿀 수도 있음
 

notion image
아, 버퍼에 이런 문자열이 뭉탱이로 들어오는구나^^ 이런게 문자열로 들어오는데(담기는데), 이걸 아파치의 BR가 먼저 읽는거임
 

2. 톰캣

notion image
필터를 통과해서 들어오면, '파일 찾기'를 한다. 파일찾기는 '파일 요청'이 들어오면 webapp (정적)으로, '식별자 요청'이 들어오면 '톰캣' (동적)으로 보내는 '조건문'이 존재한다. 아파치는 정적 파일이 오면 webapp에 가서 요청 받은 파일을 찾은 후 버퍼에 담아서 클라이언트에 돌려보낸다. 식별자 요청이 오면 아파치는, '와.. 이거 내가 할 수 있는 일이 아니다;' 라며 톰캣에게 위임한다.
톰캣도 톰캣만의 버퍼드 리더가 있다. (요청을 읽어야하니까!) (아파치랑 톰캣은 서로 다른 프로세스라서, 프로세스간의 통신을 해야함) 프로세스끼리 통신하려면 OS를 타고 내려가야 한다. (프로세스는 OS까지 타고 내려가면 됨) (OS를 타고 내려갔다 = 버퍼가 있다) (* 다른 컴퓨터랑 통신 할 때는 하드웨어까지 내려가서 통신) 아무튼, 톰캣이 받는 이 오브젝트는 아파치 언어라서 톰캣이 자기 언어로 다시 파싱을 해줘야 한다.
💡
아파치와 톰캣은 프로세스와 프로세스 통신이니 소켓이 필요 없고, 파일로 함

[ 톰캣은 자바로 만들어져서 자바 오브젝트 ]

톰캣이 아파치에서 받은 데이터를 파싱하고, 오브젝트로 변환하는데, 오브젝트로 변환을 'HTTPServletRequest', 'HTTPServletResponse' 객체로 변환한다. 1. HTTPServletRequest 객체에 버퍼드 리더에 있는 정보를 담는다. 요청한 정보는 전부!! HTTPServletRequest에 전부!! 담긴다 (메서드(GET, POST 등), 헤더, 파라미터, 쿠키, 세션 등...) 2. HTTPServletResponse 여기는 아무 것도 담기지 않고 순수한 객체를 만들어놓음. (응답할 때 사용하기 때문!! - http 통신에서 mime이랑... 담기는 거 기억하지? HTTPServletResponse는 bufferedwriter랑 연결되어 있음. bufferedwriter해서 응답할 데이터를 쓰면 됨)
클라이언트의 요청에 따라 데이터베이스에서 필요한 정보를 조회한 후, 그 결과를 응답으로 전송하는 작업은 프로그래밍(코딩)을 통해 처리해야 한다. 왜 그러냐? 클라이언트가 '응답한 정보에 따라'서 Response 해야하기 때문! 클라이언트가 요청한거에 따라 응답이 동적으로 계속 바뀌기 때문에 동적! ex. MAX값을 요청 > INSERT할때마다 MAX값이 계속 다름 > 동적 그.. Response에 담기는 것까진 프로그램이 해주는데 담은 이후(응답을 처리하고 활용하는 부분)에 뭘 할지는 우리가 코딩을 해야한다 이거... Controller랑 DAO는 우리가 해야한단 말일까?
💡
톰캣을 사용하여 자바로 작성된 웹 애플리케이션을 실행시키기 위해서는 해당 애플리케이션을 먼저 컴파일해야 한다. 이후에 톰캣에서 애플리케이션을 실행시키면, 웹 애플리케이션이 동작 함
💡
톰캣은 Request랑 Response로 요청이 온 url을 파싱하고, 거기에 해당하는 웹 서블릿을 찾아서 서비스 메소드를 호출한다. ("/*"와 같은 패턴을 사용하여 동적으로 요청)
 

 
💡
통신할 때, 1. 파싱 2. (본인의) 오브젝트로 변환 이건 불변이다
💡
여러명이 동시에 요청 → 스레드 생성 필요 원래라면 몇 명이 동시 접속했을 때까지 안터지는지, 스레드 풀을 몇 개로 제한해야 하는지… 이런걸 모두 세팅해야 한다.
근데 아파치가 해줘서 안해도 됨!!!
💡
아파치까진 웹서버. 톰캣은 WAS
 

3. Servlet (=FrontController)과 service()

클라이언트마다 요청하는게 다 다르다. 때문에 요청하는 사람마다 (요청할 때마다) 'HTTPServletRequest', 'HTTPServletResponse' 가 생긴다. 만약 'HTTPServletRequest'가 하나라면... 나는 a를 요청했는데 다른 사람이 요청한 b가 오고.. 엉망일 것 아무튼 'HTTPServletRequest', 'HTTPServletResponse' 이 2가지 정보를 가지고 Servlet 이라는 클래스가 만들어진다. (내가 new하는거 아님. 스프링이 만들어줌) 이 Servlet이 정말 중요한 메소드를 가지고 있는데 그게 바로 'service' 다.
service(HTTPServletRequest, HTTPServletResponse) { }
'HTTPServletRequest', 'HTTPServletResponse' 가 service에 이렇게 들어온다! 그럼 우린 이 service 안에 코딩만 하면 된다! 클라이언트 한명이 요청하면 Servlet 만들어지고 service메서드 때려지고 끝! 을 반복. 이렇게 만들어지는 것이다. (*개발자는 service가 끝나기 직전에 응답(Response)를 버퍼에 담아서 해줘야함) 그러나...... 문제가 있다. 클라이언트가가 요청을 다양한 주소로 할 수 있다. /hello 나 /hi 나 /bye 나 … 엄청 다양하게 요청을 할 수 있는데, 그걸 또 파싱해서, if해서.. /hello가 들어오면 뭘 하겠다. 데이터베이스 필요하면 rs=next.뭐 이런 과정들을 다 짜야한다. service 안에! 그럼 엄청나게 지저분 해지겠지? 코드가 수만줄 되겠지? (메인에다 코드 다 때려박는거랑 비슷하다고 생각하라) 그래서 레이어가 나옴. (Dispacher - Controller - DAO 이런...) 뭐... 레이어들 없이 Servlet에 다 때려박아도 되긴 한다. 감당할 수 있다면...
 

[ web.xml 파일로 서블릿 매핑 ] → 역사 속으로 사라짐

web.xml 파일 (문지기가 하는 일이 적혀있는 것)은 서블릿과 요청 URL 간의 매핑을 설정하는 역할을 한다. ex) web.xml 파일에 /hello URL에 대한 요청이 들어오면 AServlet을 실행하고, /home URL에 대한 요청이 들어오면 BServlet을 실행하도록 설정할 수 있다 그러면 어? /hello네? 하면서 문지기쪽에서 조건문 필요 없이 'HTTPServletRequest', 'HTTPServletResponse' 쪽으로 바로 슝 날아감 근데..문제가… 서블렛이 엄청나게 늘어나서 메모리가 엄청 늘어남 그래서 이걸 역사속에서 지웁니다. -> 안 쓴답니다^^
 

[ 그러니까 서블릿에 다 때려넣자고! FrontController 패턴 ]

서블릿을 나누는게 아니라, 무슨 요청이든 하나의 서블렛으로 보내서 (서블릿을 하나로 모아서) if로 처리해야겠다! 그래서 하나의 서블릿에 모든 요청을 다!! 때려넣음!! (중앙에서 요청 처리) 그래서 서블릿 = 프론트 컨트롤러 라고도 부른다 (*요청이 들어올 때마다 서블렛이 하나씩 만들어지면 이건 프론트 컨트롤러라고 안함) 근데..이렇게 하려면 라우터가 꼭 필요하고 코드를 if 로 다 짜놔야한다는 단점이... /hello 요청오면 hello 메소드 때리고, /home 요청오면 내가 home 메소드를 직접 때리고! 코드 복잡 + 유지보수도 어렵고... 그래서 리플렉션을 사용!!! 리플렉션은 실행 시간(runtime)에 클래스의 정보를 분석하고, 메소드를 동적으로 호출할 수 있는 기능. 이를 통해 프론트 컨트롤러에서 요청에 해당하는 메소드를 동적으로 호출할 수 있다. (@어노테이션 붙여서 '/hello'로 오면 이 메소드를 실행하라 이런...) 따라서, 매 요청마다 서블릿을 직접 생성하거나 if문으로 분기 처리하는 번거로움을 줄일 수 있다.
회사마다 service내부를 적는 방법이 다를 수가 있음. 그래서 나온게 스프링 프레임 워크임. 강제성을 부여해주는 것 이 프레임대로 코드 짜라!! 하는 것. 그럼 실수도 적고 회사마다 비스무리하겠지
💡
[ 분기 처리 ] 프로그램에서 "만약 A라면 이렇게 하고, 아니라면 저렇게 해야지!"라고 결정하는 것
 

 

3-1. 디스패처 호출 - DispatcherServlet

클라이언트의 요청이 들어오면 FrontController가 해당 요청을 받아서 처리하기 전에 디스패처(Dispatcher)를 호출한다. (스프링이 디스패처 클래스를 만들어주는데, 이걸 DispatcherServlet이라고 함) 이 DispatcherServlet은 요청을 분석하고, 어노테이션을 보고 라우팅(Routing)을 수행하며 해당 요청에 맞는 적절한 컨트롤러를 선택하여 호출한다.
return은 디스패처에게… 디스패처 서블릿은 클라이언트의 요청을 받아들이고, 이 요청을 처리할 적절한 컨트롤러 메서드를 실행한다. 컨트롤러 메서드는 요청을 처리한 후에 결과를 반환하는데, 이 반환된 결과는 디스패처 서블릿이 다시 클라이언트에게 전달한다. 즉, 클라이언트는 디스패처 서블릿을 통해 컨트롤러의 처리 결과를 받아본다! 컨트롤러는 자기일을 다 하고나면(DAO에게 받은 결과?) 응답을 디스패처에게 하겠지 (return을 디스패처에게.....) 컨트롤러에서 try-catch를 하잖아? throw하잖아? 누구한테 위임하는거임 = 디스패처!
💡
디스패처 서블릿은 리플렉션을 사용하여 주소와 컨트롤러 메서드 간의 매핑을 구현 (= 디스패처는 리플렉션(+어노테이션)으로 구현되어 있다. 이걸 보고 컨트롤러를 때림)
💡
서블릿을 new를 한건 톰캣이다. service메서드를 때려주는 것도 톰캣이다! (톰캣이라는 프로그램이 서블릿을 만들어준다.)
 

4. FrontController 장점 / 단점

[ 장점 ]

1. 진입점을 한 곳으로 모으면 메모리 용량을 효율적으로 관리 가능 2. 공통 로직(프로그램이나 시스템에서 수행되는 작업의 절차나 조건) 처리가 좋아짐 모든 서블릿에 공통으로 적용되어야 하는 로직이 있다면, 진입점에서 처리 가능! ex) 모든 물에 빨간색 물감을 칠하고 싶다고 가정하자 한군데에서 물감을 풀면 다 적용되어서 퍼지잖아. = 공통 로직 처리가 좋아진 것

[ 단점 ]

내가 직접 라우터를 설계하고 주소를 해석해야 한다는 단점 (라우터가 꼭 필요함)
 

매핑

매핑(Mapping)은 서블렛과 요청 URL 간의 연결. 서블렛은 웹 애플리케이션에서 클라이언트의 요청을 처리하기 위해 사용되는데, 이때 서블렛과 어떤 URL이 연결되어야 하는지를 매핑을 통해 설정한다. (매핑은 web.xml 파일이나 어노테이션을 사용하여 설정) (클라이언트의 요청과 처리 단계를 연결하는 것)
 

우린 컨트롤러랑 DAO를 만들면 된다

톰캣은 리스폰스랑 리퀘스트 그걸 만들어서 서블릿을 호출해주는 역할임 리퀘스트랑 리스폰스를 데리고가서 디스패쳐를 치니까, 매개변수를 줘야한다 내 메서드 (리스폰스,리퀘스트) 이렇게 적기만하면 리플렉션으로 해줌...!
 
💡
프론트 컨트롤러와 디스패처 서블렛을 스프링이 다 만들어줌 (눈에 안보일 뿐…) 그니까 우리가 하는건 Controller, DAO…
💡
Servlet을 주로 FrontController로 만든다. 디스패처 서블릿 = 스프링 프레임워크에서 제공하는 서블릿으로, FrontController의 역할을 수행 즉… 다 같은 걸 가리킨다.
Share article

codingb