tomcat file 설치

  • mac
    • setup.sh
  • Window
    • setup.bat 실행

종료

  • ./shutdown.sh

톰캣

  • Servlet interface(Spec)구현체
  • Servlet Container 중 하나

컨테이너 기반의 가상화 vs 하이퍼바이저 기반의 가상화(OS 가상화)

도커

  • 컨테이너 기반의 가상화

도커 허브

  • 도커에서 제공하는 이미지 저장소

도커 컴포즈

  • 다중 컨테이너를 정의하고 실행하기 위한 도구
  • YAML 파일을 사용하여 다중 컨테이너를 구성함

Info

YAML

docker command docker run —name mysql-test -e MYSWL_ROOT_PASSWORD=1234 -d -p 3309:3306 mysql:latest

  • docker run
    • 도커 실행
  • —name mysql-test
    • 도커 컨테이너 네임 mysql-test로 지정
  • -e MYSQL_ROOT_PASSWORD=1234
    • -e 환경 변수 설정
    • 비밀번호를 1234
  • -d
    • 백그라운드 실행
  • -p 3309:3306
    • 포트 설정
    • 인바운드 포트 3309
    • 아웃바운드 포트 3306
  • mysql:latest - 사용할 도커 이미지 명

docker ps

  • 실행중인 컨테이너 출력

docker docker exec -it mysql-mvc bash

  • exec
    • Execute
    • 실행
  • -it
    • interactive
    • 표준 입력 활성화
    • tty(가상 터미널)

TDD

  • Test Driven Development
  • 테스트 주도 개발
  • 프로덕션 코드보다 테스트를 먼저 작성하는 개발 방법
  • TFD(Test First Development) + 리팩토링
  • 기능 동작을 검증

BDD

  • Behavior Driven Development
  • 행위 주도 개발
  • 시나리오 기반으로 테스트 코드를 작성하는 개발 방법
  • 하나의 시나리오는 given , when , then 구조를 가짐

테스트 코드

  • assertThatCode
    • 무조건 성공해야하는 코드
  • assertThatThrownBy
    • 무조건 에러가 발생해야하는 코드
    • 에러가 발생하지 않으면 실패

람다식(익명함수)

  • (타입 매개변수 , … ) { 실행문; } ex) int sum(int a , int b) (a,b) return a+b;

생략 조건 매개변수의 타입이 생략 가능하다.

  • MaxNumber maxNumber = (str) → {System.out.println(str);}; 매개변수가 한개인 경우 소괄호 생략이 가능하다.
  • MaxNumber maxNumber = str → {System.out.println(str);}; 코드블록 내 실행하는 코드가 한줄인 경우 중괄호 생략이 가능하다.
  • MaxNumber maxNumber = (str) → System.out.println(str); 드블록 내 실행하는 코드가 return문만 있는 경우 중괄호와 return의 생략이 가능하다.
  • MaxNumber maxNumber = (str) → { return String.valueOf(str);};
  • MaxNumber maxNumber = (str) → String.valueOf(str); 매개변수가 없는 경우 소괄호만 표시해준다.
  • MaxNumber maxNumber = () → System.out.println(“매개변수 없음”);
@ParameterizedTest     
@ValueSource( strings = { "aaa" , "aaa" })
void testCode(String str){
 
}
  • @ParameterizedTest
    • org.junit.jupiter:junit-jupiter-params:5.7.0
    • 의존성을 추가해 주어야 사용 가능
    • 여러 값을 동시에 테스트 할 때 사용
    • ValueSource , CSVSource , enum 등 여러가지 사용 가능
  • @ValueSource
    • 리터럴 값의 배열에 대한 접근을 제공하는 어노테이션

테스트 코드 실습

  • RandomPasswordGenerator 를 통해서 만들 때
    • interface 를 추가하여 같은 메소드를 사용하게 한다 그리고 인터페이스를 상속 받은 케이스 2가지 ( Correct , Wrong)
      • 장점 느슨한 결합을 만들 수 있다
  • 내 생각 :

객체지향

  • 추상화
  • 다형성
  • 캡슐화
  • 상속

추상화

  • Abstraction
  • 불필요한 부분을 제거하고 핵심만 나타낸 것 ? 다형성
  • Polymorphism
  • 하나의 타입으로 여러 객체를 참조하는 것 ? 캡슐화
  • Encapsulation
  • 객체의 내부를 외부로부터 감추는 것
  • 인터페이스만 공개해서 변경하기 쉬운 코드를 만들기 위함 상속
  • Inheritance
  • 부모로부터 물려받는 것

SOLID

  • SRP : 단일 책임 원칙
  • OCP : 개방 폐쇄의 원칙
  • LSP : 리스코프 치환의 원칙
  • ISP : 인터페이스 분리의 원칙
  • DIP : 의존성 역전의 원칙

SRP

  • Single Responsibility Principle
  • 단일 책임 원칙
  • 하나의 책임을 가짐 OCP
  • Open/Closed Principle
  • 개방 폐쇄의 원칙
  • 확장에는 열려있고 변경에는 닫혀있다
  • 기존 코드를 변경하지 않고 기능을 추가 할 수 있어야 한다 LSP
  • Liskov’s Substitution Principle
  • 리스코프 치환의 원칙
  • 상위 타입의 객체를 하위 타입의 객체로 치환해도 동작에 문제가 없어야 한다 ISP
  • Interface Segregation Principle
  • 인터페이스 분리의 원칙
  • 많은 기능을 가진 인터페이스를 작은 단위로 분리
  • 클라이언트에게 필요한 인터페이스만 구현 DIP
  • Dependency Inversion Principle
  • 의존성 역전의 원칙
  • 의존 관계를 맺을 때 자주 변경되는 쪽이 아니라 거의 일어나지 않는 쪽에 의존
  • 추상화 된 인터페이스나 상위 클래스를 둬서 영향을 덜 받게 한다

객체지향 패러다임

  • 적절한 객체에게 적절한 책임을 할당하여 서로 메세지를 주고 받으며 협력하도록 하는 것
  • 점점 증가하는 SW 복잡도를 낮추기 위해 객체지향 패러다임 대두
  • 개인적으로 생각하는 두 가지 중요 포인트
    • 클래스가 아닌 객체에 초점을 맞추는 것
    • 객체들에게 얼마나 적절한 역할과 책임을 할당하는지

절차지향 프로그래밍 vs 객체지향 프로그래밍

  • 다른 객체에서 불러오는지 아닌지 차이?

cohesion

  • 결합도 coupling
  • 응집도

high cohesion, loose coupling 변경이 생겼을 때 관련 기능이 한 곳에 모여있고 영향(변경으로 인한 수정해야 할 부분)이 적음

@ParameterizedTest  
@MethodSource("calMethod")  
public void primeterizedTest(int operand1, String operator , int operand2, int result){  
    int calResult = Calculator.calculator(operand1, operator, operand2);  
    assertThat(calResult).isEqualTo(result);  
}  
  
public static Stream<Arguments> calMethod(){  
    return Stream.of(  
       Arguments.arguments(1,"+",2,3),  
       Arguments.arguments(1,"-",2,-1),  
       Arguments.arguments(2,"*",3,6),  
       Arguments.arguments(4,"/",2,2)  
    );  
  
}

메소드를 이용하여 한번에 테스트 할 때 위와 같이 사용한다.

package com.gooot;  
  
import java.util.Arrays;  
  
public enum ArithmeticOperator {  
    ADDITION("+") {  
       @Override  
       public int arithmeticCalculate(int operand1, int operand2) {  
          return operand1 + operand2;  
       }  
    }, SUBTRACTION("-") {  
       @Override  
       public int arithmeticCalculate(int operand1, int operand2) {  
          return operand1 - operand2;  
       }  
    }, MULTIPLICATION("*") {  
       @Override  
       public int arithmeticCalculate(int operand1, int operand2) {  
          return operand1 * operand2;  
       }  
    }, DIVISION("/") {  
       @Override  
       public int arithmeticCalculate(int operand1, int operand2) {  
          return operand1 / operand2;  
       }  
    };  
  
    private final String operator;  
  
  
    ArithmeticOperator(String operator) {  
       this.operator = operator;  
    }  
  
    public abstract  int arithmeticCalculate(final int operand1, final int operand2);  
  
    public static int calculate(int operand1,String operator, int operand2) {  
       ArithmeticOperator arithmeticOperator = Arrays.stream(values())  
          .filter(v->v.operator.equals(operator))  
          .findFirst()  
          .orElseThrow(()-> new IllegalArgumentException(operator+" is not supported"));  
  
       return arithmeticOperator.arithmeticCalculate(operand1,operand2);  
    }  
}

enum class 안에 추상 메소드 만들고 각 상수가 특정 클래스의 인스턴스를 생성하는 방식으로 구현할 수 있음

public class Calculator {  
  
    public static int calculator(int operand1, String operator, int operand2) {  
  
       return ArithmeticOperator.calculate(operand1, operator, operand2);

학점계산기

과목(corse)

문제점

// (학점수 * 교과목 평점)의 합계  
double multipliedCreditAndCourseGrade=0;  
for(Course course : courses) {  
    multipliedCreditAndCourseGrade += course.getCredit() * course.getGradeToNumber();  
}
  • 과목에서 학점을 계산하는게 아니라 gradeCalculate 에서 계산하고 있음
  • 해당 부분의 로직이 여러 곳에서 사용된다면 모두 바꿔줘야함 ?
  • 응집도가 약하다
  • getter로 가져와서 처리하는 것 보다 해당 정보를 가진 객체에세 메세지를 줘서 작업을 하게 함

일급 컬렉션

  • 변수 1개
  • List 로 된 객체만 가지는 클래스?

요리

객체들 끼리 비교 할 때는 Equals and HashCode 가 있어야 됨

assertThatCode(()-> menu.choose("통닭"))  
    .isInstanceOf(IllegalArgumentException.class)  
    .hasMessage("잘못된 메뉴입니다.");
    
  • assertThatCode() :
  • isInstanceOf() :
  • hasMessage("") : 메세지 확인

HTTP

  • 서버와 클라이언트가 웹에서 데이터를 주고 받기 위한 프로토콜(규약)

  • HTTP/1.1 , HTTP/2

    • TCP 기반
  • HTTP/3

    • UDP 기반
  • 클라이언트 서버 모델

  • 무상태 프로토콜(StateLess)

    • 서버가 클라이언트 상태를 유지하지 않음
    • 해결책 : Keep-Alive 속성 사용 단점 : 스레드 부족으로 다른 유저를 못 받음
  • 비 연결성

    • Connectionless
    • 서버가 클라이언트 요청에 대해 응답을 마치면 맺었던 연결을 끊어 버림
    • 해결책 : 쿠키(클라이언트에 정보 저장) , 세션(서버에 정보 저장), JWT

무상태와 비 연결성 이유

  • 불특정 다수와 통신이 가능하도록 설계된 프로토콜
  • 다수의 클라이언트들의 상태 또는 연결을 유지시 리소스 낭비가 심함
  • 상태 또는 연결을 유지하지 않는 대신 더 많은 연결을 할 수 있도록 설계 됨

Web 계산기 만들기 (Requst 만들기)

GET http://localhost:8080/calculate?operand1=11&operator=*&oerand2=55

HTTP

  • RequestLine 부분의 값을 추출해서 계산

여기서 HTTPRequest의 구조

  • RequestLine
    • HttpMethod
    • path
    • queryString

// 나누기(/)는 디코딩 해서 써야됨 안그러면 %2F 로 들어옴

Thead 알아보고 정리하기


메인스레드 처리 스레드 생성해서 할당

메인 스레드 처리

public void start() throws IOException {  
  
    try(ServerSocket serverSocket = new ServerSocket(port)){  
       logger.info("[CustomWebApplicationServer] started {} port",port);  
  
       Socket clientSocket;  
       logger.info("[CustomWebApplicationServer] waiting for client connection");  
  
       while((clientSocket = serverSocket.accept()) != null){  
          logger.info("[CustomWebApplicationServer] client connected");  
  
           try(InputStream in = clientSocket.getInputStream();  
           OutputStream out = clientSocket.getOutputStream()){  
              BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));  
              DataOutputStream dos = new DataOutputStream(out);  
  
              HttpRequest httpRequest = new HttpRequest(br);  
  
              // GET /calculate?operand1=11&operator=*&operand=55  
              if(httpRequest.isGetRequest() && httpRequest.matchPath("/calculate")){  
                 QueryStrings queryStrings = httpRequest.getQueryStrings();  
  
                 int operand1 = Integer.parseInt(queryStrings.getValue("operand1"));  
                 String operator = queryStrings.getValue("operator");  
                 int operand2 = Integer.parseInt(queryStrings.getValue("operand2"));  
  
                 int result = Calculator.calculator(new PositiveNumber(operand1),operator,new PositiveNumber(operand2));  
  
                 byte[] body = String.valueOf(result).getBytes();  
  
                 HttpResponse response = new HttpResponse(dos);  
                 response.response200Header("application/json", body.length);  
                 response.responseBody(body);  
  
              }  
  
  
  
  
  
  
  
  
              // String line;  
              // while((line = br.readLine()) != ""){                //  System.out.println(line);              // }           }  
  
       }  
  
    }  
  
}

요청이 들어오면 스레드 생성

implements Runnable

Runnable 인터페이스를 구현해서 새로운 스레드에서 실행

ClientRequestHandler 구현

package com.gooot;  
  
import java.io.BufferedReader;  
import java.io.DataOutputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.InputStreamReader;  
import java.io.OutputStream;  
import java.net.Socket;  
import java.nio.charset.StandardCharsets;  
  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
  
import com.gooot.calculate.Calculator;  
import com.gooot.calculate.HttpResponse;  
import com.gooot.calculate.PositiveNumber;  
  
public class ClientRequestHandler implements Runnable {  
  
    private static final Logger logger = LoggerFactory.getLogger(ClientRequestHandler.class);  
  
    private final Socket clientSocket;  
  
    public ClientRequestHandler(Socket clientSocket) {  
       this.clientSocket = clientSocket;  
    }  
  
  
    @Override  
    public void run() {  
  
       logger.info("[ClientRequestHandler] new client {} started.", Thread.currentThread().getName());  
  
       try (InputStream in = clientSocket.getInputStream();  
           OutputStream out = clientSocket.getOutputStream()) {  
          BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));  
          DataOutputStream dos = new DataOutputStream(out);  
  
          HttpRequest httpRequest = new HttpRequest(br);  
  
          // GET /calculate?operand1=11&operator=*&operand=55  
          if (httpRequest.isGetRequest() && httpRequest.matchPath("/calculate")) {  
             QueryStrings queryStrings = httpRequest.getQueryStrings();  
  
             int operand1 = Integer.parseInt(queryStrings.getValue("operand1"));  
             String operator = queryStrings.getValue("operator");  
             int operand2 = Integer.parseInt(queryStrings.getValue("operand2"));  
  
             int result = Calculator.calculator(new PositiveNumber(operand1), operator,  
                new PositiveNumber(operand2));  
  
             byte[] body = String.valueOf(result).getBytes();  
  
             HttpResponse response = new HttpResponse(dos);  
             response.response200Header("application/json", body.length);  
             response.responseBody(body);  
  
          }  
       } catch (IOException e) {  
          logger.error(e.getMessage(), e);  
       }  
    }  
}
public void start() throws IOException {  
  
    try (ServerSocket serverSocket = new ServerSocket(port)) {  
       logger.info("[CustomWebApplicationServer] started {} port", port);  
  
       Socket clientSocket;  
       logger.info("[CustomWebApplicationServer] waiting for client connection");  
  
       while ((clientSocket = serverSocket.accept()) != null) {  
          logger.info("[CustomWebApplicationServer] client connected");  
  
          new Thread(new ClientRequestHandler(clientSocket)).start();  
  
 
       }  
  
    }  
  
}

문제점 :

  • 스레드는 생성 될 때마다 메모리를 할당 받음 이 작업은 굉장히 비싼 작업이
  • 동시 접속자가 많아질 경우
    • CPU ContextSwitching 증가
    • CPU 사용량 증가
    • 메모리 사용량 증가
    • 결론 : 서버가 다운 될 가능성이 있음
  • 스레드풀
    • 스레드를 고정된 갯수만큼 생성
    • 재활용

private final ExecutorService executor = Executors.newFixedThreadPool(10);
 
executor.execute(new ClientRequestHandler(clientSocket));
 

스레드풀을 이용해서 스레드 갯수 제한 


CGI

  • Common Gateway Interface
  • 웹 서버와 애플리케이션 사이에 데이터를 주고받는 규약
  • CGI 규칙에 따라서 만들어진 프로그램을 CGI 프로그램이라고 함
  • CGI프로그램 종류로는 컴파일 방식(C,C++,JAVA 등)과 인터프리터 방식(PHP,Python 등)이 있음

웹서버에서 스크립트 엔진을 실행시키고 스크립트 파일을 해석해서 값을 리턴

서블릿

  • Servlet
  • Server + Applet 의 합성어
  • 자바에서 웹 애플리케이션을 만드는 기술
  • 자바에서 동적인 웹 페이지를 구현하기 위한 표준

서블릿 컨테이너

  • ServletContainer
  • 서블릿의 생성부터 소멸까지 라이프 사이클을 관리하는 역할
  • 서블릿 컨테이너는 웹 서버와 소켓을 만들고 통신하는 과정을 대신 처리해준다. 따라서, 개발자는 비즈니스 로직에만 집중하면 된다.
  • 서블릿 객체를 싱글톤으로 관리
    • 상태를 유지(Stateful)하게 설계하면 안됨
    • Thread safety 하지 않음

상태를 유지하게 되면 안됨

  • 멀티스레드 환경에서 하나의 객체를 공유 ( 싱글턴 ) 시
  • 뜻하지않는 결과가 나옴
  • 레이스 컨디션 : 여러 프로세스 혹은 스레드가 동시에 하나의 자원에 접근하기 위해 경쟁하는 상태를 레이스 컨디션이라고함

동기화 처리로 개선 가능

@Override  
public void run() {  
    synchronized (this) {  
       this.increment();  
       System.out.println(  
          "Value of Thread After increment " + Thread.currentThread().getName() + " " + this.getValue());  
       this.decrement();  
       System.out.println("Value of Thread at last " + Thread.currentThread().getName() + " " + this.getValue());  
    }

Spring Bean

  • 디폴트로 싱글턴으로 관리됨
  • 상태를 유지하게 설계하면 안됨
  • ”상태를 유지한다”는 말은 프로그램이나 시스템이 이전의 상태 정보를 기억하고, 이를 기반으로 동작을 계속할 수 있다는 의미

엔진 컨테이너 차이

  • 엔진 : ?
  • 컨테이너 : 라이프 사이클을 관리

WAS vs 서블릿 컨테이너

  • WAS는 서블릿 컨테이너를 포함하는 개념
  • WAS는 매 요청마다 스레드 풀에서 기존 스레드를 사용함
  • WAS의 주요 튜닝 포인트는 max thread 수
  • 대표적인 WAS로는 톰캣이 있다.


Servlet 인터페이스

  • 서블릿 컨테이너가 서블릿 인터페이스에 있는 메소드들을 호출함
  • 서블릿 생명주기와 관련된 메소스
    • init() , service() , destroy()
  • 서블릿 기타 메소드
    • getServletConfig()
    • getServletInfo()

Servlet

  • 필요가 없어도 일일이 구현을 해줘야 됨 ??

GenericServlet

  • init , destroy , getServletConfig , getServletInfo 필요할 때만 Overide

HttpServlet

  • 요청마다 구현해주면 됨
    • doGet(), doDelete , doPost

GenericServlet vs HttpServlet

URL 인코딩 (=퍼센트 인코딩)

  • URL로 사용할 수 없는 문자 (예약어 , Non-ASC2 문자(한글)등)를 사용할 수 있도록 인코딩하는 것
  • 인코딩 된 문자는 triplet(세 개가 한 세트)로 인코딩 되며 각각을 % 다음에 두 개의 16진수로 표현함
    • , *
  • 예약 문자

Spring MVC Flow


JDBC

  • Java DataBase Connectivity
  • 자바 애플리케이션에서 DB 프로그래밍을 할 수 있도록 도와주는 표준 인터페이스
  • JDBC 인터페이스들을 구현한 구현체들은 각 데이터 베이스 벤더 사들이 제공

DBCP

  • 미리 일정량의 DB 커넥션을 생성해서 풀에 저장해 두고 있다가 HTTP 요청에 따라 필요할 때 풀에서 커넥션을 가져다 사용하는 기법
  • 참고로 스프링 부트 2.0 부터는 디폴트 커넥션 풀로 HikariCP 사용

주의 사항

  • 커넥션의 사용 주체는 WAS 스레드
  • 커넥션 갯수는 WAS 스레드 수와 함께 고려해야 함
  • 커넥션 수를 크게 설정하면
    • 메모리 소모 증가
    • 접속자가 많더라도 사용자 대기시간 감소
  • 커넥션 수를 작게 설정하면
    • 메모리 소모 적음
    • 대기 시간이 길어짐
  • 적정량 커넥션 객체를 생성해야됨

DataSource

  • 커넥션을 획득하기 위한 표준 인터페이스
  • HikariCP 의 DataSource 사용

try With Resource 하면 자동으로 자원해제함

  • rs.close()
  • psmt.close()
  • con.close()

connectionPool 적용

con = ConnectionManager.getConnection();  
String sql = "INSERT INTO USERS VALUES (?,?,?,?)";  
psmt = con.prepareStatement(sql);

변경되는 쿼리문 외부로부터 받아오면 됨

String sql = "INSERT INTO USERS VALUES (?,?,?,?)";  
psmt = con.prepareStatement(sql);  
psmt.setString(1, user.getUserId());

psmt 는 외부로부터 전달 받을려면 2번 줄 때문에 안됨

변경되는 부분은 호출자 입장에서 전달 


Reflection

  • 힙 영역에 로드되어 있는 클래스 타입의 객체를 통해 필드 / 메소드 / 생성자를 접근 제어자와 상관 없이 사용할 수 있도록 지원하는 API
  • 컴파일 시점이 아닌 런타임 시점에 동적으로 특정 클래스의 정보를 추출해낼 수 있는 프로그래밍 기법
  • 주로 프레임워크 또는 라이브러리 개발 시 사용 됨
  • Spring 프레임워크 ( DI, AOP etc )

프런트 컨트롤러 패턴

  • 모든 요청을 단일 handler(처리기) 에서 처리하도록 하는 패턴
  • 스프링 웹 MVC 프레임워크의 DispatcherServlet (프런트 컨트롤러 역할)이 프런트컨트롤러 패턴으로 구현되어 있음

Forwad

  • 서블릿에서 클라이언트 ( 웹 브라우저 )를 거치지 않고 바로 다른 서블릿(또는 JSP)에게 요청하는 방식
  • Forward 방식은 서버 내부에서 일어나는 요청이기 때문에 HttpServletRequest, HttpServletResponse 객체가 새롭게 생성되지 않음 ( 공유 됨 )
  • RequestDispatcher dispatcher = request.getRequestDispatcher( ” 포워드 할 서블릿 또는 JSP ” )

Redirect

  • 서블릿이 클라이언트 ( 웹 브라우저 )를 다시 거쳐 다른 서블릿 ( 또는 JSP )에게 요청하는 방식
  • Redirect 방식은 클라이언트로부터 새로운 요청이기 때문에 새로운 HttpServletRequest, HttpServletResponse 객체가 생성됨
  • HttpServletResponse 객체의 sendRedirect() 이용

@WebServlet("/")  
public class DispatcherServlet extends HttpServlet {  
  
    private static final Logger log = LoggerFactory.getLogger(DispatcherServlet.class);  
  
    private RequestMappingHandlerMapping requestMappingHandlerMapping;  
  
    @Override  
    public void init() throws ServletException {  
       requestMappingHandlerMapping.init();  
    }

톰캣이 HttpServlet을 싱글톤으로 생성 requestHandlerMapping 이 만들어질 때 초기화 되는 코드

에러 :

The absolute uri: [http://java.sun.com/jsp/jstl/core] cannot be resolved in either web.xml or the jar files deployed with this application

출처: https://beagle-dev.tistory.com/82 [언젠간 되어있겠지:티스토리]

  1. <%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %> 이 JSP 에 추가 되지 않았거나

  2. 이와 관련된 라이브러리가 추가되지 않았기 때문.

https://tomcat.apache.org/download-taglibs.cgi

위 링크에서 다운로드후 lib 폴더를 WEB_INF 폴더 바로 아래에 추가!

출처: https://beagle-dev.tistory.com/82 [언젠간 되어있겠지:티스토리]


MVC 프레임 워크 만들기

어노테이션 기반 MVC 프레임 워크

  • DispatcherServlet
  • AnnotationHandlerMapping
  • HandlerAdapter
  • ViewResolver

public Controller findHandler(String uriPath){  
    return mappings.get(uriPath);  
}

DispatcherServlet 의 문제점

  • uri로만 구분하기 때문에 requestMethod를 구분 할 수 없음
  • 해결 : HandlerKey Class 추가

command - P

  • 인텔리제이 인자값 보기