본문 바로가기

JAVA

Java & SpringBoot로 시작하는 웹 프로그래밍 : 자바 인강 - 5주차

  • HTTP 요청과 응답
  • 클라이언트와 서버
  • 설정 파일 - server.xml, web.xml
  • 텍스트와 바이너리
  • MINE
  • Base64
  • 관심사의 분리와 MVC패턴

 

 

Chapter 2. Spring MVC

HTTP 요청과 응답 - 실습 

프로그램 실행 결과를 톰캣이 아니라 브라우저에 출력

자바 안터프리터가 2021 10 1을 자동으로 배열로 만들어준다.

 

년월일을 입력하면 해당 날짜가 어떤 요일인지 알려주는 프로그램

 

target 에 컴파일링 결과마 만들어짐

targer 우클릭 - Show In - Terminal

package com.fastcamput.ch2;

import java.util.Calendar;

// 년일월을 입력하면 요일을 알려주는 프로그
public class YoilTeller {

	public static void main(String[] args) {
		
		// 1. 입력
		String year = args[0];
		String month = args[1];
		String day = args[2];
		
		int yyyy = Integer.parseInt(year);
		int mm = Integer.parseInt(month);
		int dd = Integer.parseInt(day);
		
		 // 2. 작업
		Calendar cal = Calendar.getInstance();
		cal.set(yyyy, mm - 1, dd);
		
		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
		char yoil = "일월화수목금토".charAt(dayOfWeek);
		
		// 3. 출력
		System.out.println(year + "년 " + month + "월 " + day + "일은 ");
		System.out.println(yoil + "요일입니다.");
	}

}

 target우클릭 - Show In - terminal 로 실행 후 

cd classes 엔터 후 java com.fastcampus.ch2(패키지명).ch2.YoilTeller 2021 10 1 (매개변수 값 세 개 입력)

 

( STS 버전이 3.9.17이라 그런지 실행이 안되고 STS가 강제 종료되는데 검색해보니 버전 18에서는 괜찮은 것으로 보임..

마이너한 문제라 그냥 넘어가도 된다는 답변 발견..)

 

 

HTTP 요청과 응답

1. HttpServletRequest

원격 프로그램을 브라우저로 URL을 입력해서 호출하면 톰캣이 받아서 HttpServletRequest 객체를 만들고 요청한 정보를 담은 후

메인 메소드의 매개변수 public void main(여기!)에 넘겨줌 그러면 자동으로 알아서 넣어준다.

public void main(HttpServletRequest request) 에서 request 객체를 사용해서 요청한 URL 정보를 얻을 수 있다.

즉, 요청할 때 입력한 URL 정보를 톰캣이 객체를 만들고 거기에 담아서 주는 것 우리는 그걸 사용하면 된다.

 

2. HttpServletRequest의 메소드

자주 쓰이는 메소드들

package com.fastcamput.ch2;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

@Controller
public class RequestInfo {
    @RequestMapping("/requestInfo")
    //    public static void main(String[] args) {
    public void main(HttpServletRequest request) {
        System.out.println("request.getCharacterEncoding()="+request.getCharacterEncoding()); // 요청 내용의 인코딩
        System.out.println("request.getContentLength()="+request.getContentLength());  // 요청 내용의 길이. 알수 없을 때는 -1
        System.out.println("request.getContentType()="+request.getContentType()); // 요청 내용의 타입. 알 수 없을 때는 null

        System.out.println("request.getMethod()="+request.getMethod());      // 요청 방법
        System.out.println("request.getProtocol()="+request.getProtocol());  // 프로토콜의 종류와 버젼 HTTP/1.1
        System.out.println("request.getScheme()="+request.getScheme());      // 프로토콜

        System.out.println("request.getServerName()="+request.getServerName()); // 서버 이름 또는 ip주소
        System.out.println("request.getServerPort()="+request.getServerPort()); // 서버 포트
        System.out.println("request.getRequestURL()="+request.getRequestURL()); // 요청 URL
        System.out.println("request.getRequestURI()="+request.getRequestURI()); // 요청 URI

        System.out.println("request.getContextPath()="+request.getContextPath()); // context path
        System.out.println("request.getServletPath()="+request.getServletPath()); // servlet path
        System.out.println("request.getQueryString()="+request.getQueryString()); // 쿼리 스트링

        System.out.println("request.getLocalName()="+request.getLocalName()); // 로컬 이름
        System.out.println("request.getLocalPort()="+request.getLocalPort()); // 로컬 포트

        System.out.println("request.getRemoteAddr()="+request.getRemoteAddr()); // 원격 ip주소
        System.out.println("request.getRemoteHost()="+request.getRemoteHost()); // 원격 호스트 또는 ip주소
        System.out.println("request.getRemotePort()="+request.getRemotePort()); // 원격 포트
    }
}

실행시 ch2 우클릭 - Run As - 1 Run on Server

브라우저에 localhost:8080/ch2/requestInfo 치면 STS 콘솔에 출력됨

어떠한 메소드가 어떤 값을 반환하는지 알 수 있다.

& 구분자로 해서 값을 여러개 보낼 수 있다

getParameter메소드를 쓰면 문자열이 하나씩 들어간다.

 

 

YoilTellar.java를 로컬 프로그램에서 원격 호출 가능한 프로그램으로 만들어보기

클래스 이름 바꿀 때 클래스 우클릭 - Refactor - Rename

package com.fastcampus.ch2;

import java.util.Calendar;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

// 년일월을 입력하면 요일을 알려주는 프로그램
@Controller
public class YoilTeller {
//	public static void main(String[] args){
	@RequestMapping("getYoil")
	public void main(HttpServletRequest request) {
		
		// 1. 입력
		String year = request.getParameter("year");
		String month = request.getParameter("month");
		String day = request.getParameter("day");
		
		int yyyy = Integer.parseInt(year);
		int mm = Integer.parseInt(month);
		int dd = Integer.parseInt(day);
		
		 // 2. 작업
		Calendar cal = Calendar.getInstance();
		cal.set(yyyy, mm - 1, dd);
		
		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
		char yoil = "일월화수목금토".charAt(dayOfWeek);
		
		// 3. 출력
		System.out.println(year + "년 " + month + "월 " + day + "일은 ");
		System.out.println(yoil + "요일입니다.");
	}
	
}

위 코드는 3단계 실행

넘어온 값을 입력값을 받고 입력받은 것을 작업, 그 후 결과 출력

 

실행시 젤 위 ch2 우클릭 - Run As - 1 Run on Server

http://localhost:8080/ch2/getYoil?year=2021&month=10&day=1

열린 톰캣 사이트에 입력

잘 출력된 모습 !

 

 

브라우저에 결과가 나오게 하려면 response 객체를 매개변수로 적어주기

package com.fastcampus.ch2;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

// 년일월을 입력하면 요일을 알려주는 프로그램
@Controller
public class YoilTeller {
//	public static void main(String[] args){
	@RequestMapping("getYoil")
	public void main(HttpServletRequest request, HttpServletResponse response) throws IOException{
		
		// 1. 입력
		String year = request.getParameter("year");
		String month = request.getParameter("month");
		String day = request.getParameter("day");
		
		int yyyy = Integer.parseInt(year);
		int mm = Integer.parseInt(month);
		int dd = Integer.parseInt(day);
		
		 // 2. 작업
		Calendar cal = Calendar.getInstance();
		cal.set(yyyy, mm - 1, dd);
		
		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
		char yoil = "일월화수목금토".charAt(dayOfWeek);
		
		// 3. 출력
		response.setContentType("text/html"); // 출력할 내용의 타입 쓰기
		response.setCharacterEncoding("utf-8"); // 텍스트의 인코딩 알려주기
		PrintWriter out = response.getWriter(); // response 객체에서 브로우저로의 출력 스트림을 얻는다.
		out.println(year + "년 " + month + "월 " + day + "일은 ");
		out.println(yoil + "요일입니다.");
	}
	
}

코드 수정 해주기

HttpServletResponse response 객체를 적어주면 이 객체도 톰캣이 만들어서 준다.

그것을 사용해서 브라우저에 출력이 가능한 것

콘솔이 아니라 브라우저에 결과가 잘 출력되는 모습 !

 

 

화면에 브라우저 주사위 보여지게 하기

주사위 이미지 파일

dice.zip
0.08MB

package com.fastcampus.ch2;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller 
public class TwoDice {

	@RequestMapping("/rollDice")
	public void main(HttpServletResponse response) throws IOException{
		
		response.setContentType("text/html");
		response.setCharacterEncoding("utf-8");
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<head>");
		out.println("</head>");
		out.println("<body>");
		out.println("<img src = 'resources/img/dice1.jpg'>");
		out.println("<img src = 'resources/img/dice2.jpg'>");
		out.println("</body>");
		out.println("</html>");
	}
}

ctrl+shift+o 는 자동으로 import

주사위 이미지 폴더 resource에 붙여넣기

print문으로 html 형식의 텍스트를 출력만 해주면 된다.

출력 후 http://localhost:8080/ch2/rollDice 입력시 결과

 

 

누를 때 마다 주사위 다른 이미지 나오게 하기

package com.fastcampus.ch2;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller 
public class TwoDice {

	@RequestMapping("/rollDice")
	public void main(HttpServletResponse response) throws IOException{
		
		// 실행 할 때마다 주사위 결과 달라지게 하기 
		int idx1 = (int)(Math.random()*6) + 1;
		int idx2 = (int)(Math.random()*6) + 1;
		
		response.setContentType("text/html");
		response.setCharacterEncoding("utf-8");
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<head>");
		out.println("</head>");
		out.println("<body>");
		out.println("<img src = 'resources/img/dice"+idx1+".jpg'>");
		out.println("<img src = 'resources/img/dice"+idx2+".jpg'>");
		out.println("</body>");
		out.println("</html>");
	}
}

 

출력 결과

새로고침 할 때마다 결과가 달라짐을 알 수 있다. 

-> 동적 리소스

img에 있는 이미지 파일 -> 정적 리소스

 

 

서버가 제공하는 리소스의 종류

  • 동적 리소스 : 리소스 내용이 고정되어있지 않은 것(프로그램이 생성하는 결과, 스트리밍)
  • 정적 리소스 : 파일 형태로 되어있고 바뀌지 않는 것 (이미지, gs파일, cs파일, html)

 

HTTP 요청과 응답

1. HttpServletRequest

브라우저에 URL을 입력해서 요청을 하면 해당 서버에 있는 톰캣이 요청을 받아 객체 생성 후 정보를 나눠서 저장

그 후 public void main(HttpServletRequest request)의 매개변수 request로 넘겨준다.

그러면 메소드 안에서 객체를 이용하여 원하는 정보를 얻을 수 있다.

 

2. HttpServletRequest 메소드

 

요청할 때 ? 뒤에 getQueryString() 메소드에 추가적인 데이터를 보낼 수 있다. 

getQueryString() 값을 읽을려면 getParameter라는 메소드를 이용해야한다. 

getQueryString() 에는 name 과 value 간의 쌍으로 표시되어 있다.

getParameter("name")으로 얻어오게 되는데

전부 String이라 문자열이기 때문에 숫자로 얻으려면 Integer.parseInt(year)를 쓰면 된다.

 

이름만 가져오려면 getParameter 메소드 사용

Enumeration은 Iterator와 비슷 (Enumeration이 예전 버전)

 

맵 형태로 가져올 수 있는 것

key value
"year" 2021
"month" 10
"day" 1

이런식으로 map에 담아 반환해준다.

 

name이 다 같은 경우에는 getParameterValues 를 사용

name이 year인 값들을 배열로 받을 수 있다.

 

String 배열에 저장되고 yearArr의 참조변수로 다룰 수 있다.

 

 

 

클라이언트와 서버

1. 클라이언트와 서버 (역할에 따른 구분)

  • 클라이언트 : 서비스를 서버에게 요청하는 어플리케이션 (브라우저는 클라이언트 어플리케이션)
  • 서버 : 서비스를 제공하는 어플리케이션 (톰캣은 서버 어플리케이션)

브라우저로 클라이언트가 프로그램을 호출해서 요청하면

서버는 프로그램을 실행하고 결과가 텍스트 문서 <html>로 나와 브라우저로 보내준다.

 

 

2. 서버의 종류

서버의 종류는 어떤 서비스를 제공하는지에 따라 달라진다.

  • Email 서버는 Email 서비스를 제공
  • File 서버는 File을 제공
  • Web 서버는 Web을 제공 (Web은 브라우저를 통해 받을 수 있는 모든 서비스)

 

3. 서버의 포트

하나의 PC에 서버 프로그램이 여러가지 있는 경우

클라이언트가 IP주소로 요청시 어떤 서버인지 구별할 수 없다.

이런 경우 포트번호가 필요하다.

IP주소와 함께 포트번호를 보내게 되면 클라이언트가 어떤 서버에 요청보내는지 구별할 수 있다

(Web서버는 기본으로 80이고 생략 가능하다.)

한 포트에는 한 서버만 연결되어있고 Listening상태로 요청을 기다리고 있다.

0~1023은 예약되어 있어 사용하지 못하고 그 후 6만개의 포트를 사용할 수 있다.

 

 

4. 웹 어플리케이션 서버(WAS)란?

웹 어플리케이션을 서비스하는 서버

어플리케이션(프로그램)을 서비스한다는 것은 서버에 프로그램을 설치해놓고

클라이언트가 이 프로그램을 사용할 수 있게 해준다.

톰캣을 WAS(Web Application Server)라고 부른다.

우리가 만든 프로그램을 클라이언트가 원격 호출한다.

 

 

5. Tomcat의 내부구조

기본적인 Tomcat의 구조

브라우저로 요청하면 톰캣이 8080이라 8080으로 간다.

그러면 사용자 요청을 처리할 Thread Pool이 기다리고 있음

미리 Thread Pool을 만들어놓고 요청이 오면 한가한 Thread가 요청을 처리

 

톰캣 서버안에는 서비스가 있음 

서비스가 요청을 처리하는데 그 안에 Connector가 있음

Connector은 요청한 프로토콜 종류에 따라 달라진다.

그러면 요청한 프로토콜에 맞는 Connector가 처리하는데 

이 Connector가 Engine(Catalina)한테 요청을 전달

그 안엔 여러개의 Host가 있다.

Host안에는 Context가 있는데 Context 하나하나가 Web Application

즉, 하나의 Host안에 여러개의 Web Application이 설치될 수 있다.

그 하나하나가 STS project에 해당

STS project는 서로 영향을 주지 않는 독립적인 공간에서 돌아가는 것

그 Context안에 있는 서블릿은 Servlet으로 작은 서버에서 실행되는 프로그램이라는 뜻

서블릿과 컨트롤러는 같은 개념

그래서 서버프로그램 여러개가 같은 Context안에서 돌아갈 수 있다.

즉, 서블릿이 작업을 수행하고 클라이언트한테 작업 결과를 전달한다

 

 

이 과정을 실습으로 보기

19번째 줄에서 Toggle Breakpoint(중단점) 누르기

Breakpoint : 실행하다가 중단점에서 멈춰놓고 상황을 보기

우클릭 Debug As에서 1 Debug on Server 클릭

 

 

http://localhost:8080/ch2/rollDice 입력하면

스레드가 여러개 있는데 그 중 하나가 요청을 받아 처리한 것

밑에 쭉 있는건 맨 아래 부터 호출 스택이 쌓인 것

 

 

ThreadPool에서 Http11Processor, Engine, Host, Context 순서로 실행되는 것을 알 수 있다.

Http11Processor가 요청을 받아서 Request, Response를 처음 만들어서 계속 전달한다 !

Context 까지 받은 후

그 안에서 서블릿을 전처리하는 필터를 거친다. 결국을 서블릿이 받은 후 컨트롤러를 호출

(Dispatcher Servlet이 받음)

TwoDice라는 컨트롤러가 받아서 main메소드가 호출된다.

 

가볍게 이렇게 흘러간다고 알고 넘어가기!

 

 

6. Tomcat의 설정 파일 - serer.xml, web.xml

  • 톰캣설치경로 / conf / server.xml : Tomcat 서버 설정 파일
  • 톰캣설치경로 / conf / web.xml : Tomcat의 모든 web app 공통 설정
  • 웹앱이름 / WEB-INF / web.xml : web app의 개별 설정(context 프로젝트마다 하나씩 있다.)

 

톰캣의 설정 파일들

 

STS에서 개별적으로 톰캣 설정 파일들을 복사해서 쓰는 것이라 설정을 따로 할 수 있다.

서버를 따로 따로 설정할 수 있는 것이 장점

이 파일들은 Tomcat의 conf 폴더에 있는 설정 파일의 복사본으로

하나의 Tomcat 프로그램을 공유하면서 설정만 다른 여러 서버를 등록 가능

 

Servers - Tomcat v9.0 Server at localhost-config - server.xml - Source 보기

HTTP / 1.1 프로토콜이 요청이 오면 이 Connector가 처리해라는 뜻이고

8080 포트에서 요청 받으려고 대기중

 

Engine은 여러 Host 포함 가능, 그 중에서 어떤 Host를 default로 할 것인지 지정 가능

여기서는 localHost를 default로 지정 가능

 

1번 web.xml 파일 : 개별 설정

2번 web.xml 파일 : 모든 Web app 공통 설정

 

모든 Web.app 공통 설정하는 web.xml의 소스코드

원격 프로그램의 서블릿 등록하는 부분 (그 후 URL 연결)

이걸 간단하게 @Controller와 @RequestMapping으로 바꿈 -> 애너테이션

(사실 @Controller와 @RequestMapping은 스프링에서만 사용하고

서블릿에서는 @WebServlet을 사용)

 

default라는 이름의 Sevlet을 특정 주소 패턴과 매핑

 

 

 

개별설정 web.xml

 

 

HTTP 요쳥과 응답

1. 프로토콜(protocol) 이란?

서로 간의 통신을 위한 약속, 규칙

주고 받을 데이터에 대한 형식(서로 간의 약속)을 정의한 것

그 형식을 토대로 해석함

 

2. HTTP (Hyper Text Tranfer Protocol) 란?

텍스트를 전송하기 위한 프로토콜

  • 텍스트이기 때문에 단순하고 읽기 쉽다.
  • 상태를 유지하지 않는다 (stateless) : 클라이언트 정보를 저장하지 않는다

ㄴ같은 클라이언트가 요청을 두 번 보내도 같은 클라이언트인지 모른다. (쿠키, 세션을 사용하면 저장 가능)

  • 확장 가능 (헤더와 바디로 이뤄져있는데 표준에 정의되지 않은 커스텀 헤더 추가 가능)

 

3. HTTP 메세지

HTTP 메세지는 편지와 비슷 (서로에게 요청 편지를 보내고 상대방은 응답 편지를 보내는 것과 비슷)

헤더(본문에 대한 설명)와 바디(실제 전달할 내용)로 구성되어 있다.

  • 실제로 URL을 입력하면 브라우저가 요청 메세지를 자동으로 만들어 보냄
  • 브라우저가 만들어진 메세지가 서버에 도착하고 서버는 요청에 대한 응답

 

 

4. HTTP 메세지 - 응답 메세지

응답 메세지 첫번째 줄 : 상태 라인 status line (요청이 어떻게 처리되었는지 알려줌)

상태코드 의미
1xx Informational (클라이언트와 서버간의 정보 교환이 목적)
2xx Success (요청 성공)
3xx Redirect (다른 URL로 요청해라)
4xx Client Error
5xx Server Error

 

 

5. HTTP 메서드 - 요청 메세지

요청 메세제는 대표적으로 GET, POST 로 구성되어 있다.

  • GET은 URL 뒤, 쿼리스트링에 데이터를 포함 시켜 서버에 전송
  • POST는 바디에 데이터를 포함시켜 서버에 전송

 

 

6. HTTP 메서드 - GET, POST

GET POST
서버한테 리소스를 요청해서 얻어오려고 하는 것, 읽기 위한 것 (Read)
바디가 없어 추가적으로 데이터를 보내고 싶을 때 쿼리스트링으로 보냄
바디가 있어서 서버에 전송할 데이터를 바디에 담고 있다.
글쓰기, 로그인, 파일 첨부 등 posting 쓰기 위한 것 (Write)
-서버의 리소스를 가져오기 위해 설계
-QUERY STRING을 통해 데이터를 전달 (소용량)
-URL에 데이터 노출되므로 보안에 취약 (바디 없음)
-데이터 공유에 유리 (다른 사람한테 전달하기 쉽다)

ex) 검색엔진에서 검색단어 전송에 이용
-서버에 데이터를 올리기 위해 설계
-전송 데이터 크기의 제한이 없음 (대용량)
-데이터 요청 메세지의 body에 담아 전송
-보안에 유리, 데이터 공유에는 불리

ex) 게시판에 글쓰기, 로그인, 회원가입

 

GET 요청 실습 

YoilTeller 실행 ch2 - Run As - Run on Server1

http://localhost:8080/ch2/getYoil?year=2021&month=10&day=1 입력 후

우클릭 - 검사 - Network - ctrl + R(새로고침)

요청한 것에 대한 헤더를 볼 수 있다.

Request Header가 우리가 요청한 헤더

Request Header의 View source 클릭시 요청을 어떻게 갖는지 알 수 있다.

 

General 헤더는 요청, 응답 헤더를 뺀 일반헤더 (요청과 응답에 대한 공통적인 헤더)

 

 

GET, POST 요청 postman으로 실습 

1. GET 요청

<form>을 써야하는데

post man 이라는 확장 프로그램 설치시 GET, POST 방식 외에도 요청할 수 있고

반복적인 요청도 가능하다.

구글 - 크롬 확장프로그램 - Chrome 웹 스토어 - postman 검색 - Tabbed Postman - REST Client 검색 - 핀 추가

http://localhost:8080/ch2/getYoil (URL)만 복사 - Postman에 붙여넣기 - Get에 URL params 파라미터 주기 - Send

 

2. POST 요청

Post로 바꾸기 -  x-www-form-urlencoded 선택 -  파라미터 입력 - Preview 누르면 요청이 이렇게 갈 거라고 뜸

POST는 데이터를 URL 뒤가 아닌 바디에 넣기 때문에 바디에 들어간 모습

 

 

7. 텍스트 파일 vs 바이너리 파일

  • 바이너리 파일 : 문자와 숫자(data)가 저장되어 있는 파일, 데이터를 있는 그대로 읽고 쓴다
  • 텍스트 파일 : 문자만 저장되어 있는 파일, 숫자를 문자로 변환 후 쓴다

바이너리 파일과 텍스트 파일을 구별하려면 메모장을 열어봤을 때 

읽을 수 있으면 텍스트 파일, 깨져서 읽기 어려울 때 바이너리 파일

 

파일 종류 쓰기 읽기
바이너리 문자 -> 문자
숫자 -> 숫자
문자 -> 문자
숫자 -> 숫자
텍스트 문자 -> 문자
숫자 -> 문자 (변환)
문자 -> 문자
문자 -> 숫자 (변환)

 

 

8. MIME (Multipurpose Internet Mail Extensions)

  • 텍스트 기반 프로토콜에 바이너리 데이터를 전송하기 위해 고안
  • HTTP의 Content-Type 헤더에 이용
  • 전송할 데이터의 타입을 명시

ex) response.SetContentType("text/html") 이런 식으로 명시해야

브라우저가 응답 받았을 때 해석을 똑바로 할 수 있다.

 

 

바이너리를 서버에게 전송시 어떻게 전송되는지 실습

 

Preview 선택시 바이너리 데이터를 첨부로 보낼 때 어떤 식으로 요청이 가는지 알 수 있다.

 

전체가 요청 메세지이며 binary data 와 text data 가 하나의 메세지에 같이 보내는 중

단, postman의 한계로 chrome에서 자동으로 보내주는 헤더는 전부 다 나오지 않는다.

 

 

요청시 요청한 헤더 정보를 다 출력해주는 프로그램

package com.fastcampus.ch2;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RequestHeader {
	@RequestMapping("/requestHeader")
	public void main(HttpServletRequest request) {
		
		Enumeration<String> e = request.getHeaderNames();

		while (e.hasMoreElements()) {
			String name = e.nextElement();
			System.out.println(name + ":" + request.getHeader(name));
		}
	}
}

 

코드 추가 후 실행하고 http://localhost:8080/ch2/requestHeader 입력 결과

요청 메세지의 헤더 정보

헤더 정보가 쭉 나온 걸 알 수 있다

 

 

헤더뿐만 아니라 메세지 전체를 보여주는 예제

package com.fastcampus.ch2;

import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RequestMessage {
	@RequestMapping("/requestMessage")
	public void main(HttpServletRequest request) throws Exception {
		
		// 1. request line
		String requestLine = request.getMethod();       // GET 또는 POST
		requestLine += " " + request.getRequestURI();   // /ch2/requestMessage
		
		String queryString = request.getQueryString();  // year=2021&month=10&day=1
		requestLine += queryString == null ? "" : "?"+queryString;  
		requestLine += " " + request.getProtocol();     // HTTP/1.1
		System.out.println(requestLine);		

		
		// 2. request headers
		Enumeration<String> e = request.getHeaderNames();

		while (e.hasMoreElements()) {
			String name = e.nextElement();
			System.out.println(name + ":" + request.getHeader(name));
		}
		
		// 3. request body - POST일 때만 해당, GET은 body가 없음(CONTENT_LENGTH=0)
		final int CONTENT_LENGTH = request.getContentLength();
//		System.out.println("content length="+CONTENT_LENGTH);
		
		if(CONTENT_LENGTH > 0) {
			byte[] content = new byte[CONTENT_LENGTH];

			InputStream in = request.getInputStream();
			in.read(content, 0, CONTENT_LENGTH);
			
			System.out.println(); // empty line
			System.out.println(new String(content, "utf-8")); // year=2021&month=10&day=1
		}  // if
	} // main
}

 

 

9. Base 64 (64진법)

  • 바이너리 데이터를 텍스트 데이터로 변환할 때 사용 (그대로 보내는 MINE과는 다른 방법)
  • 64진법은 '0' ~ '9', 'A' ~ 'Z', 'a' ~ 'z', '+', '/' 모두 64개의 문자로 구성
  • 6bit씩 잘라서 비트를 보고 문자(8bit)로 변환한다.
  • 안전하게 주고받을 수 있지만 데이터가 늘어나는 것이 단점

 

실습

구글 - base64 encode 검색 - base64encode.net 접속 - 우측 상단 Image to Base64

- 파일 선택 후 encode 해서 나온 결과 복사 - vscode에 새로운 파일 만들어 붙여넣기

- ! 엔터나 html:5 입력 - <body>에 img태그 만들기

- "data:image/jpeg;base64, 붙여넣기 alt = ""> 치고 encode 결과 붙여 넣기 - 저장

- 우클릭 - Open in Default Browser

결과가 잘 출력된 모습

바이너리 파일이 텍스트 파일로 들어간 것

하나의 html파일과 연결된 이미지를 링크 걱정 없이 넣을 수 있다.

base64 encoding 해서 이미지를 텍스트로 넣은 것

 

 

 

관심사의 분리, MVC 패턴 - 이론

1. 관심사의 분리 Separation of Concerns

관심사(Concern) : 위 YoilTeller프로그램에서 입력, 요일 계산, 출력 부분으로 크게 3개의 부분으로 나눠져있다.

이 하나하나를 관심사라고 한다. 관심을 갖고 해야할 작업이라고 생각하면 된다.

 

OOP 5대 설계원칙 : SOLID 중 첫번째 SRP - 단일 책임 원칙

하나의 메소드는 하나의 책임만 진다. 즉, 관심사는 한 개만 있어야한다.

프로그램이 돌아갈지라도 관심사가 하나만 있도록 분리를 해야한다. 

 

 

분리의 세 가지 기준

  1. 관심사
  2. 변하는것, 자주 변하지 않는 것
  3. 공통코드

 

 

2. 공통 코드의 분리 - 입력의 분리

 

getYoil 메소드에서 입력 받는 부분 분리한 모습

@RequestMapping("/getYoil")
	public void main(HttpServletRequest request, HttpServletResponse response) throws IOException{
		
		String year = request.getParameter("year");
		String month = request.getParameter("month");
		String day = request.getParameter("day");
		
		int yyyy = Integer.parseInt(year);
		int mm = Integer.parseInt(month);
		int dd = Integer.parseInt(day);

@RequestMapping("/getYoil")
	public void main(String year, String month, String day, HttpServletResponse response) throws IOException{
		
		int yyyy = Integer.parseInt(year);
		int mm = Integer.parseInt(month);
		int dd = Integer.parseInt(day);

 

매개변수로 request 객체를 받아와서 그 값들을 year, month, day가 가진 값 2020, 10, 1을 꺼내서 썼는데 

개별 매개변수로 직접 받도록 바꿀 수 있다. 그러면 getParameter 부분이 필요 없음

String 대신 int로 쓸 수 있다.(int형으로 자동으로 변환하여 변환된 값을 넣어줌)

 

 

 

3. 출력(view)의 분리 - 변하는 것과 변하지 않는 것의 분리

처리, 출력 역할의 관심사를 분리하게 되면 같은 메소드 안에 있어 접근 가능했던 변수가 다른 메소드에선 읽을 수 없다.

서로 다른 영역이라 두 관심사를 연결해줄 중간 객체 (모델 객체)가 필요하다.

객체를 하나 만들어 놓고 결과에 필요한 값을 다 저장한 후 

이 객체에 담긴 내용들을 읽어서 출력 가능

즉, 코드를 분리했기 때문에 분리한 두 코드간에 데이터를 전달하기 위해 Model이 필요하다.

 

MVC 패턴

: 처리하는 부분 Controller

출력하는 부분 View

이 두 부분간의 데이터를 주고받을수 있는 객체 Model

 

DispatcherServlet 

  1. 입력 & 변환
  2. 모델 객체 생성 (결과 저장)

 

 

4. getYoil 프로그램을 관심사의 분리, MVC 패턴 - 실습

1) 입력의 분리한 결과

package com.fastcampus.ch2;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

// 년일월을 입력하면 요일을 알려주는 프로그램
@Controller
public class YoilTellerMVC { // http://localhost:8080/ch2/getYoilMVC?year=2021&month=10&day=1
//	public static void main(String[] args){
	@RequestMapping("/getYoilMVC")
//	public void main(HttpServletRequest request, HttpServletResponse response) throws IOException{
	public void main(int year, int month, int day, HttpServletResponse response) throws IOException{	

		Calendar cal = Calendar.getInstance();
		cal.set(year, month - 1, day);
		
		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); // 1:일요일, 2:월요일..
		char yoil = "일월화수목금토".charAt(dayOfWeek);
		
		// 3. 출력
		response.setContentType("text/html"); // 출력할 내용의 타입 쓰기
		response.setCharacterEncoding("utf-8"); // 텍스트의 인코딩 알려주기
		PrintWriter out = response.getWriter(); // response 객체에서 브로우저로의 출력 스트림을 얻는다.
		out.println("<html>");
		out.println("<head>");
		out.println("</head>");
		out.println("<body>");
		out.println(year + "년 " + month + "월 " + day + "일은 ");
		out.println(yoil + "요일입니다.");
		out.println("</body>");
		out.println("</html>");
	}
	
}

 

2) 출력의 분리까지한 결과

View만 모아둔 것

home.jsp를 복붙해서 yoil.jsp 만들기

 

yoil.jsp

<%@ page contentType = "text/html;charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
</head>
<body>

<P> ${year }년 ${month }월 ${day }일은 ${yoil }입니다. </P>
</body>
</html>

모델 객체가 가지고 있는 값이 들어갈 자리를 ${   }으로 만들어둔 것을

EL(Expression Language)라고 한다.

 

yoilError.jsp

<%@ page contentType = "text/html;charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
	<title>Home</title>
</head>
<body>
<h1>
	잘못된 요청입니다. 년, 월, 일을 모두 올바르게 입력해주세요.
</h1>

</body>
</html>

Save as UTF-8로 저장하기

 

별도의 메소드로 빼는 방법

뺄 부분을 모두 선택 하고 우클릭 - Refacotr - Extract Method 

자동으로 private 메소드가 만들어진다.

 

yoilTellerMVC.java 코드

package com.fastcampus.ch2;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

// 년일월을 입력하면 요일을 알려주는 프로그램
@Controller
public class YoilTellerMVC { // http://localhost:8080/ch2/getYoilMVC?year=2021&month=10&day=1
//	public static void main(String[] args){
	@RequestMapping("/getYoilMVC")
//	public void main(HttpServletRequest request, HttpServletResponse response) throws IOException{
	public String main(int year, int month, int day, Model model, HttpServletResponse response) throws IOException{	

		// 1. year, month, day가 잘 들어왔는지 유효성 검사
		if(!isValid(year, month, day))
			return "yoilError";
		
		// 2. 요일 계산
		char yoil = getYoil(year, month, day);
	
		// 3. 계산한 결과를 model에 저장
		model.addAttribute("year", year);
		model.addAttribute("month", month);
		model.addAttribute("day", day);
		model.addAttribute("yoil", yoil);
		
		
		return "yoil"; 
		// "yoil"이라는 출력 결과를 보여주고 싶은 View를 반환, 반환타입 있으니 void 말고 String으로
		// /WEB-INF/views/yoil.jsp를 이용해서 작업 경로를 지정
		
	}

private boolean isValid(int year, int month, int day) {
	// TODO Auto-generated method stub
	return true;
}

private char getYoil(int year, int month, int day) {
	Calendar cal = Calendar.getInstance();
	cal.set(year, month - 1, day);
	
	int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); // 1:일요일, 2:월요일..
	return "일월화수목금토".charAt(dayOfWeek);
}
	
}

입력 받을 값들을 매개변수로 선언하고 모델도 매개변수로 선언한 후 

작업을 처리한 다음에 모델의 작업 결과를 저장하여

이 작업 결과를 보여줄 view를 반환해줌

그러면 DispatcherServlet이 작업 결과가 저장된 모델을 view에 전달

그러면 view는 이 데이터가 담겨있는 모델 객체에서 값을 읽어서 최종결과를 만들어 보내줌

 

결과

오류인 경우

잘나온 경우

yoil이라는 view의 이름이 어떻게 바껴있는지 보기

둘 다 Spring 설정 파일

spring context.xml : 웹 관련된 설정

 

servlet-context.xml에서 view의 경로를 지정하는 부분

 

여러가지 변형도 가능

- void main으로 반환타입을 안주면 맵핑된 주소의 jsp로 해석이 된다.

- 만약 view와 URL의 이름이 같으면 return만 빠지면 view이름이 바꼈을 때 URL만 바꿔주면 된다.

 

 

5. ModelAndView

반환 타입을 ModelAndView로 주는 경우

Model과 View를 합치는 것 

매개변수로 Model을 받지않고 코드 안에서 Model을 만드는 것

메소드는 하나의 값만 반환해야하는데 이 경우, Model과 View를 같이 반환해야하기 때문에 

둘 다 담는 객체를 만들고 model에 결과 저장하고 view를 지정해서 같이 넘겨주는 것

package com.fastcampus.ch2;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

// 년일월을 입력하면 요일을 알려주는 프로그램
@Controller
public class YoilTellerMVC { // http://localhost:8080/ch2/getYoilMVC?year=2021&month=10&day=1
//	public static void main(String[] args){
	@RequestMapping("/getYoilMVC")
//	public void main(HttpServletRequest request, HttpServletResponse response) throws IOException{
	public ModelAndView main(int year, int month, int day, Model model, HttpServletResponse response) throws IOException{	

		ModelAndView mv = new ModelAndView();
		
		// 1. 유효성 검사
//		if(!isValid(year, month, day))
//			return "yoilError";
		
		// 2. 요일 계산
		char yoil = getYoil(year, month, day);
	
		// 3. 계산한 결과를 model에 저장
		mv.addObject("year", year);
		mv.addObject("month", month);
		mv.addObject("day", day);
		mv.addObject("yoil", yoil);
				
		// 4. 결과를 보여줄 view를 지정
		mv.setViewName("yoil");
	
		return mv;
	}

private boolean isValid(int year, int month, int day) {
	// TODO Auto-generated method stub
	return true;
}

private char getYoil(int year, int month, int day) {
	Calendar cal = Calendar.getInstance();
	cal.set(year, month - 1, day);
	
	int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); // 1:일요일, 2:월요일..
	return "일월화수목금토".charAt(dayOfWeek);
}
	
}

ModelAndView는 잘 쓰이지는 않는다.

 

 

6. 컨트롤러 메서드의 반환타입

public □□□ main 에서 □□□에

1. String : 뷰 이름을 반환 ex) return "yoil";

2. void : 뷰 이름을 반환하지 않고 맵핑된 url의 끝 단어가 뷰 이름

ex) @RequestMapping("/yoil")

3. ModelAndView : 컨트롤러 내부에서 ModelAndView를 만들고 

작업 결과를 저장한 후 View이름을 지정한 후 ModelAndView를 반환

 

 

 

 

5주차 후기

오류때메 진짜 개고생했다...하ㅏ하하하