서블릿은 웹 서버의 요청을 받아서 동적인 페이지를 생성하고 반환하는 과정을 편리하게 처리할 수 있도록 지원하는 자바 웹 프로그래밍 기술이다.

 

What Is a Servlet?

A servlet is a Java programming language class that is used to extend the capabilities of servers that host applications accessed by means of a request-response programming model. Although servlets can respond to any type of request, they are commonly used to extend the applications hosted by web servers. For such applications, Java Servlet technology defines HTTP-specific servlet classes.

The javax.servlet and javax.servlet.http packages provide interfaces and classes for writing servlets. All servlets must implement the Servlet interface, which defines life-cycle methods. When implementing a generic service, you can use or extend the GenericServlet class provided with the Java Servlet API. The HttpServlet class provides methods, such as doGet and doPost, for handling HTTP-specific services.

This chapter focuses on writing servlets that generate responses to HTTP requests.

 

서블릿은 인터페이스를 구현해서 사용해야 하는데 init(), destroy()와 같이 생명 주기를 관리하는 메서드가 있으며 일반적으로 기본적인 틀을 제공하는 GenericServlet을 상속 받아서 service() 추상 메서드를 구현한다.

 

HTTP 요청과 관련된 메소드를 제공하는 HttpServlet을 보면 GenericServlet을 상속 받아서 service()를 Override했다.

 

서블릿은 독립적으로 생성, 실행될 수 없고 관리를 해주는 무언가가 있어야 하는데 이러한 역할을 해주는 것이 서블릿 컨테이너이며 대표적으로 서블릿 컨테이너를 구현한 WAS(Web Application Server)인 톰캣이 있다. (Tomcat = Web Server + Servlet Container)

 

서블릿 컨테이너는 서블릿의 생명 주기를 관리하고 웹서버와 소켓으로 통신하며 요청을 받아서 응답을 전달하는 다양한 기능을 제공한다.

 

멀티 스레드를 지원하는 서블릿 컨테이너는 요청을 받으면 스레드를 생성하고 Request, Response 객체를 생성해서 서블릿의 service()를 실행한뒤 스레드를 종료시킨다. 이때, 서블릿은 요청 시마다 생성 되지 않고 최초 1회만 생성이 된다. 

 

위에 사진은 GenericServlet의 service() 추상 메서드로 HttpServlet은 GenericServlet을 상속 받아서 이 service() 메서드를 Http 요청을 처리하도록 구현하였다.

 

정리를 하면 서블릿 컨테이너가 웹 서버로부터 요청을 받으면 Request, Response 객체를 생성하고 매핑된 서블릿을 찾아서 service 메서드를 실행한다. 

 

ServletWebServerFactory servletWebServerFactory = new TomcatServletWebServerFactory();

WebServer webServer = servletWebServerFactory.getWebServer(servletContext -> {
    servletContext.addServlet("myServlet", new HttpServlet() {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //...
        }
    }).addMapping("/hello");
});

 

이렇게 각각의 서블릿에 url 매핑을 하면 중복되는 코드도 많고 비즈니스 로직과 뷰 로직이 결합 되어 있어서 유지보수 하기 어려운 코드가 된다. 그래서 MVC 패턴과 스프링MVC가 나오게 되는데 모든 요청을 받아서 공통 로직을 처리하고 컨트롤러의 핸들러 역할을 하는 프론트 컨트롤러인 DispatcherServlet이 있다.

 

 

처음 스프링 부트로 개발을 하면 기본적으로 톰캣이 내장되어 있고 서블릿 관련 설정을 지원해주기 때문에 이해가 부족한 경우가 많은데 코드 몽키가 되지 않으려면 잘 알아야 될 것 같다..!

 

다음에는 DispatcherServlet에 대해 정리를 해야겠다.

 

[참고]

 

What Is a Servlet? - The Java EE 5 Tutorial

What Is a Servlet? A servlet is a Java programming language class that is used to extend the capabilities of servers that host applications accessed by means of a request-response programming model. Although servlets can respond to any type of request, the

docs.oracle.com

인프런 김영한님, 토비님 강의

망나니개발자님 블로그

코드스쿼드 미션을 진행하던 중 사용자로부터 콘솔 입력을 받아서 값을 반환해주는 메서드를 테스트 하는 방법을 찾아보니 콘솔 입력을 값으로 넣어줄 수 있는 방법이 있었다.

 

내가 테스트한 메서드는 inputLadderHeight()로 필드 변수인 scanner를 통해 입력을 받고 있다. (원래는 scanner를 static final로 선언하고 메서드도 static으로 작성했는데 변경된 상태이다.) 

 

메서드를 보면 내부에서 scanner.nextLine()으로 입력을 읽는데 Scanner를 생성할때 java.lang 패키지의 System.in을 파라미터로 전달을 한다. 설명을 보면 이 표준 InputStream은 이미 열려서 데이터를 받을 준비가 되어있고, user가 설정할 수도 있다고 나와 있다.

 

스레드 초기 설정 후에 initPhase1() 메서드에서 System class를 초기화 하는데 처음에 Systen.in이 java.io 패키지의 BufferedInputStream으로 초기화가 되는 것을 확인할 수 있다.

 

 

다시 맨 위 코드를 보면 Screen 클래스가 생성될때 Scanner도 초기화가 되는데 이때 System의 static final 변수인 in이 전달이 된다.

그래서 Screen을 생성하기 전에 System.setIn()으로 테스트용 InputStream을 set 해주면 내가 임의로 만든 테스트 입력 값을 Scanner가 읽도록 할 수 있다.

 

 

테스트 코드를 보면 InputStream을 만들어서 Scanner가 초기화 되기 전에 System.in을 교체해주면 된다.

 

 

처음에 Scanner를 static final로 선언했더니 여러번 반복 테스트를 할 때 계속 의도한 대로 동작하지가 않았다.

생각을 해보니 여러번 테스트를 할 때 InputStream을 새로 생성해서 setIn()을 해줘도 처음 Screen 클래스가 로드되면서 Scanner에 주입된 InputStream이 그대로 있어서 입력이 들어가지 않았던 것이다.

 

역시 static은 되도록 사용하지 않는 것이 좋다고 느꼈다..

 

 

 

[참고]

https://sakjung.tistory.com/33

https://www.geeksforgeeks.org/java-lang-system-class-java/

https://mommoo.tistory.com/71

문제

 

절댓값 힙은 다음과 같은 연산을 지원하는 자료구조이다.

  1. 배열에 정수 x (x ≠ 0)를 넣는다.
  2. 배열에서 절댓값이 가장 작은 값을 출력하고, 그 값을 배열에서 제거한다. 절댓값이 가장 작은 값이 여러개일 때는, 가장 작은 수를 출력하고, 그 값을 배열에서 제거한다.

첫째 줄에 연산의 개수 N(1≤N≤100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 

 

만약 x가 0이 아니라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0이라면 배열에서 절댓값이 가장 작은 값을 출력하고 그 값을 배열에서 제거하는 경우이다. (정수는 -231보다 크고, 231보다 작다.) 

만약 배열이 비어 있는 경우인데 절댓값이 가장 작은 값을 출력하라고 한 경우에는 0을 출력한다.

 


PriorityQueue를 사용하면 쉽게 풀릴 것 같은데 절댓값이 같은 경우 음수를 반환해야 하기 때문에 이부분만 따로 if문으로 조건 처리를 해주었다.

 

 

    PriorityQueue<Integer> queue = new PriorityQueue<>(Main::compareToAbs);

    int N = Integer.parseInt(br.readLine());
    for (int i = 0; i < N; i++) {
        int value = Integer.parseInt(br.readLine());

        if (value != 0) {
            queue.add(value);
            continue;
        }

        if (queue.isEmpty()) {
            sb.append("0").append("\n");
            continue;
        }

        sb.append(queue.poll()).append("\n");
    }

    System.out.println(sb);
}

private static int compareToAbs(Integer n1, Integer n2) {
    int a1 = Math.abs(n1);
    int a2 = Math.abs(n2);

    if (a1 == a2) {
        if (n1 < n2) {
            return -1;
        }
        return 1;
    }

    return a1 - a2;
}

'코딩테스트' 카테고리의 다른 글

[백준, 2164] 카드2  (0) 2023.03.05
[백준, 17298] 오큰수  (0) 2023.03.05
[백준, 1874] 스택 수열  (0) 2023.03.05
[백준, 12891] DNA 비밀번호  (0) 2023.03.05
[백준, 1940] 주몽의 명령  (0) 2023.03.04

문제

 

N장의 카드가 있다. 각각의 카드는 차례로 1부터 N까지의 번호가 붙어 있으며, 1번 카드가 제일 위에, N번 카드가 제일 아래인 상태로 순서대로 카드가 놓여 있다.

 

다음 동작을 카드가 한 장 남을 때까지 반복

-> 제일 위에 있는 카드를 바닥에 버린다. 그 다음, 제일 위에 있는 카드를 제일 아래에 있는 카드 밑으로 옮긴다.

 

 


입력 예시가 6이면 카드는 위에서부터 1,2,3,4,5,6 으로 쌓여있는 상태이다.

1을 버리고 2를 제일 밑으로 보내는 식으로 반복하면

 

1) 3,4,5,6,2

2) 5,6,2,4

3) 2,4,6

4) 6,4

 

마지막에 6을 버리면 정답은 4가 된다.

 

제일 위에 숫자를 버리고 제일 밑에 숫자를 추가하는 식의 동작이기 때문에 Deque를 사용하면 될 것 같다.

처음에 Deque가 떠올랐는데 생각해보니 그냥 Queue를 사용해도 poll하고 다시 넣으면 제일 뒤로 가기 때문에 더 나은 것 같다.

 

int N = Integer.parseInt(br.readLine());

Deque<Integer> deque = new LinkedList<>();

for (int i = 1; i <= N; i++) {
    deque.addFirst(i);
}

while (deque.size() > 1) {
    deque.removeLast();
    deque.addFirst(deque.removeLast());
}

System.out.println(deque.peek());

 

'코딩테스트' 카테고리의 다른 글

[백준, 11286] 절댓값 힙  (0) 2023.03.05
[백준, 17298] 오큰수  (0) 2023.03.05
[백준, 1874] 스택 수열  (0) 2023.03.05
[백준, 12891] DNA 비밀번호  (0) 2023.03.05
[백준, 1940] 주몽의 명령  (0) 2023.03.04

+ Recent posts