Servlet에 대한 고찰

현이·2022년 9월 22일
2

고찰

목록 보기
6/6

지금부터 JAVA WAS에서 사용중인 Servlet객체를 직접한번 만들어 볼겁니다.

우선 동작원리부터 살펴보겠습니다.

Servlet 동작원리를 설명하기 위해선, Socket통신에 대한 깊은 고찰이 필요합니다.

우선, Servlet이 나오게된 계기부터 알아보아야 합니다.

원시에 우리는 통신을 하기위해 Socket이라는 것을 사용했습니다.


Servlet에 들어가기전, 멀티쓰레드 환경이 필요한 이유에 대해 먼저 설명해 드리겠습니다.

서버가 ServerSocket을 이용하여 연결을 기다리고,
A라는 사용자가 서버에 요청을 하면 서버는 요청에 대한 응답을 해주겠죠.

그런데, A, B, C, D, E 라는사용자가 동시에 서버에 요청을 보낸다면?

서버는 먼저 들어온 요청에 대한 응답이 끝날때까지 다른 요청을 처리할 수 없기 때문에

다른 사용자는 먼저 요청한 사용자의 요청이 끝날때까지 기다려야 하는 일이 발생해요.

하지만, 서버가 요청을 받을때마다 쓰레드를 만들어서 독립적인 환경에서 실행되게 한다면?

동시에 여러개의 요청이 들어오게 되더라도, 로직을 쓰레드에 실어서 실행하면 요청에 대한 처리를 동시에 진행할 수 있어요.


자 이제 우리는 서버가 요청을 받으면, 처리로직을 쓰레드를 만들어서 실행한다는 것을 알았어요.

하지만 우리는 브라우저 통신을 할때 HTTP 프로토콜을 사용하기때문에 정해진 약속들이 많이 있어요.

하지만 Servlet은 그런 로직들을 다 조각내서 딱 우리가 원하는 로직만 짤 수 있도록 해주기 위하여 탄생했어요.

지금부터 이해를 돕기위해 소켓을통해 한번 구현해 보도록 하겠어요.


[java servlet이랑은 약간 다른점이 있습니다. 이해를 돕기위한 코드입니다.]

Porolet.class

public abstract class Porolet {

	// 현재 인스턴스가 어떤객체인지 알아보기 위해 찍어보았음.
	public void init() {
		System.out.println("----------init----------" + this.getClass().getName());
	}
	
	public abstract void service(InputStream in, PoroRes response)throws Exception;
}

HelloPorolet.class

public class HelloPorolet extends Porolet {

	@Override
	public void service(InputStream in, PoroRes response) throws Exception {
		
		response.setContentType("text/html");
		
		Thread.sleep(3000);
		
		response.print("<h1>Hello World</h1>");

	}

}

PoroRes.class

  • SocketServer에서 OutputStream으로 전달해주던 작업을 대신해줄 객체입니다.
@AllArgsConstructor
@Getter
public class PoroRes {

	private OutputStream out;
	
    // 응답헤더 설정
	public void setContentType(String type) {
		try {
			out.write(new String("HTTP/1.1 200 OK\r\n").getBytes());
			out.write(new String("Content-Type: "+ type +" \r\n\r\n").getBytes());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
    // body에 메세지 실어서 응답
	public void print(String msg) {
		try {
			out.write(msg.getBytes(StandardCharsets.UTF_8));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

이제 Server class 의 생성자에서, 우리가 만든 properties 파일을 불러와서 path와 인스턴스를 key,value 형태로 인스턴스변수(MAP)에 저장해 줍니다

Server.class 의 생성자 코드

public MainServer() {
		letMap = new HashMap<>();
		Properties prop = new Properties();
		
		try {
			prop.load(new FileInputStream("C:\\zzz\\ui.properties"));
			
			prop.keySet().forEach(key -> {
				String value = prop.getProperty((String)key);
				System.out.println(value);
				
				try {
					Class clz = Class.forName(value);
					Porolet obj = (Porolet)clz.getConstructor(null).newInstance(null);
					obj.init();
					letMap.put((String)key, obj);
					
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

클라이언트의 요청이 들어왔을때

  1. 요청을한 클라이언트 socket의 i/o stream을 가져와서 Res,Req 객체를 생성합니다.
  2. uri를 파싱해서 map변수에 path이름으로 된 key값으로 만들어진 인스턴스(value)를 가져와서 service method를 호출합니다.
public void runServer() {
		try {
			ServerSocket server = new ServerSocket(5555);
			System.out.println("server running.................");
			
			while(true) {
				
				Socket socket = server.accept();
				
				new Thread(() -> {
					try {
						InputStream in = socket.getInputStream();
						Scanner inScanner = new Scanner(in);
						OutputStream out = socket.getOutputStream();
						
						String firstLine = inScanner.nextLine();
						
						String target = firstLine.split(" ")[1];
					
						PoroRes res = new PoroRes(out);
						
						try {
							
							Porolet let = letMap.get(target);
							
							let.service(in, res);
						} catch (Exception e) {
							out.write("HTTP/1.1 500 Internel Server Error\r\n".getBytes());
						}
						in.close();
						out.close();
					}catch(Exception e) {
						
					}
				}).start();

			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
  • ui.properties
/hello=zserver.server.let.HelloPorolet
/lotto=zserver.server.let.LottoPorolet

이제 우리는 요청받을 주소를 properties에 객체와함께 설정해주고,
Porolet 추상클래스를 상속받은 객체만 만들어 추가하면 무한한 확장이 가능하게 됩니다.

profile
의미있는 고찰

0개의 댓글