1. HTTP헤더(Header)

  • General headers
      - 메시지 전체에 적용되는 헤더이며, body를 통해 전송되는 데이터와는 관련이 없는 헤더이다
  • Response headers
      - 위치 또는 서버 자체에 대한 정보(이름, 버전 등)와 같이 응답에 대한 부가적인 정보를 갖는 헤더이다
      - Vary, Accept-Ranges와 같이 상태 줄에 넣기에는 공간이 부족했던 추가 정보를 제공한다
  • Representation headers
      - Entity headers라고 부르기도 한다
      - body에 담긴 리소스의 정보(콘텐츠 길이, MIME 타입 등)를 포함하는 헤더이다

 

2. Spring MVC에서 HTTP Header의 사용

 1) 클라이언트와 서버 관점에서의 대표적인 HTTP 헤더

  • 클라이언트와 서버의 관점에서 내부적으로 가장 많이 사용되는 헤더 정보로 ‘Content-Type’이 있다
    - 클라이언트와 서버가 주고 받는 HTTP 메시지 body의 데이터 형식을 알려준다
    - 클라이언트와 서버는 Content-Type의 데이터 형식에 맞는 데이터들을 주고 받는다
  • 샘플로 작성하고 있는 커피주문 애플리케이션의 Content-Type은 ‘application/json’이다
    - Spring MVC - API 계층 - Controller (MVC 개요/핸들러 메서드) 에서는 prosuce 값으로 사용했지만, Spring MVC - API 계층 -Controller (ResponseEntity 적용) 에서 Map 객체를 사용하면서 Content-Type을 삭제했다

 

 2) 대표적인 HTTP 헤더 예시

  • 개발자가 직접 코드 레벨에서 HTTP 헤더를 컨트롤 해야될 경우 사용하는 대표적인 HTTP 헤더이다
  • Authorization 헤더
    - 클라이언트가 적절한 자격 증명을 가지고 있는지를 확인하기 위한 정보이다
    - REST API 기반 애플리케이션의 경우 클라이언트와 서버 간의 로그인(사용자 ID/비밀번호) 인증(Authenticatioin)에 통과한 클라이언트들은 ‘Authorization’ 헤더 정보를 기준으로 인증에 통과한 클라이언트가 맞는지 확인하는 인가 절차를 거친다
  • User-Agent 헤더
    - 애플리케이션을 구현 과정에서 여러가지 유형의 클라이언트가 하나의 서버 애플리케이션에 요청을 전송하는 경우가 많다
    - 사용자에 따라 데스크탑이나 노트북의 웹 브라우저를 사용하거나 스마트폰, 태블릿 등 모바일에서 서버에 요청을 보낸다
    - 보내오는 요청의 클라이언트를 구분해서 응답 데이터를 다르게 보내줘야 되는 경우가 있다
    - 모바일, 데스크탑, 노트북의 화면 크기에 따라 더 많은 정보를 보여주기 위해 각각 데이터의 종류와 크기가 다를 수 있다
    - User-Agent 헤더 정보를 이용해서 클라이언트의 요청 브라우져를 구분해서 처리할 수 있다

3. 샘플 어플리케이션에 HTTP Header 적용

 1) @RequestHeader 로 개별 헤더 정보 받기

  • CoffeeController에 적용하여 실행한다
package com.dreamfactory.exam_controller.coffee;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Member;
import java.sql.SQLOutput;
import java.util.HashMap;
import java.util.Map;


@RestController
@RequestMapping(value="v1/coffees"/*, produces = MediaType.APPLICATION_JSON_VALUE*/)
//v1은 버젼을 의미한다. coffees는 데이터 post, get 의 조회 위치를 의미한다

public class CoffeeController {
    @PostMapping

    //postCoffee() 메서드는 커피 정보를 등록해 준다
    public /*String*/ResponseEntity postCoffee(//@RequestParam("coffee")String coffee,
                                               @RequestHeader("user-agent")String userAgent,
                                                 @RequestParam("coffeeId")String coffeeId,
                                                 @RequestParam("korName")String korName,
                                                 @RequestParam("engName")String engName,
                                                 @RequestParam("price")int price) {
//        System.out.println("# coffee:" + coffee);
//        System.out.println("# coffeeId:" + coffeeId);
//        System.out.println("# korName:" + korName);
//        System.out.println("# engName:" + engName);
//        System.out.println("# price:" + price);

        //@RequestHeader 로 개별 정보 받아오기
        System.out.println("user-agent: " + userAgent);
        return new ResponseEntity<>(new Coffee(korName, engName, price),
        HttpStatus.CREATED);

        //Map객체로 변경
//            Map<String, String> map = new HashMap<>();
//            map.put("coffee", coffee);
//            map.put("coffeeId", coffeeId);
//            map.put("korName", korName);
//            map.put("engName", engName);
//            map.put("price", String.valueOf(price));
//
//            //return 값을 ResponseEntity로 변경
//        return new ResponseEntity<>(map, HttpStatus.CREATED);

//        String reponse =
//                "{\"" +
//                    "coffee\":\""+coffee+"\"," +
//                    "\"coffeeId\":\""+coffeeId+"\"," +
//                    "\"korName\":\""+korName+"\"," +
//                    "\"engName\":\""+engName+"\"," +
//                    "\"price\":\""+price+
//                "\"}";
//        return reponse;
    }

    @GetMapping("/{coffee-id}")

    //getCoffee() 메서드는 커피 정보을 클라이언트에게 제공하는 핸들러 메서드이다
    public /*String*/ResponseEntity getCoffee(@PathVariable("coffee-id")long coffeeId) {
        System.out.println("# coffeeId: " + coffeeId);
        return new ResponseEntity<>(HttpStatus.OK);
        }

    @GetMapping //별도의 URI를 지정해주지 않았기 때문에 클래스 레벨의 URI(“/v1/coffees”)에 매핑된다

    //getCoffees() 메서드는 커피 목록을 클라이언트에게 제공하는 핸들러 메서드이다
    public /*String*/ResponseEntity getCoffees() {
        System.out.println("# get Coffees");
        return new ResponseEntity<>(HttpStatus.OK);
    }
}
  • 실행하면 error가 발생한다
  • new Coffee에 대한 신규 class를 생성하여 문제를 해결한다
package com.dreamfactory.exam_controller.coffee;

public class Coffee {
    public Coffee(String korName, String engName, int price) {
    }
}

 

 2) @RequestHeader 로 전체 헤더 정보 받기

  • MemberController에 적용하여 실행한다
package com.dreamfactory.exam_controller.member;

import ch.qos.logback.classic.util.LogbackMDCAdapter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.sql.SQLOutput;

import java.util.HashMap;
import java.util.Map;

@RestController

@RequestMapping("v1/members"/*,produce=MediaType.APPLICATION_JSON_VALUE*/)

public class MemberController {
    @PostMapping

    public /*String*/ResponseEntity postMember(@RequestHeader Map<String, String> headers,
                                               @RequestParam("email")String email,
                                               @RequestParam("name")String name,
                                               @RequestParam("phone")String phone) {
       
//        System.out.println("# email: " + email);
//        System.out.println("# name: " + name);
//        System.out.println("# phone: " + phone);

//        JSON문자열 응답 타입을 수작업 코드에서 Map객체로 변경->produce애트리뷰트를 삭제할 수 있다
            //MAp 객체를 리턴하면 내부적으로 응답 데이터를 JSON데이터로 자동 변환해야 한다고 인식한다
//            Map<String, String> map = new HashMap<>();
//            map.put("email", email);
//            map.put("name", name);
//            map.put("phone", phone);

        //@RequestHeader Map을 사용하여 전체 헤더 정보를 받아 온다
        for (Map.Entry<String, String> entry : headers.entrySet()){
            System.out.println("key: " + entry.getKey() +
                    ", value: " + entry.getValue());
        }
            //리턴 값을 변경된 ResponseEntity로 대체
            //ResponseEntity 객체를 생성하고 생성자 파라미터로 map과 HttpStatus.CREATED를 반환한다
            //HttpStatus.CREATED 는 201, created를 의미한다
//            return new ResponseEntity<>(map, HttpStatus.CREATED);

        //@RequestHeader Map 을 사용함으로 리턴 값의 코드가 변경된다
        return new ResponseEntity<>(new Member(email, name, phone), HttpStatus.CREATED);

//        String reponse =
//                "{\"" +
//                    "email\":\""+email+"\"," +
//                    "\"name\":\""+name+"\",\"" +
//                    "phone\":\"" + phone+
//                "\"}";
//        return reponse;
    }

    @GetMapping("/{member-id}")

    public /*String*/ResponseEntity getMember(@PathVariable("member-id")long memberId) {
       
        System.out.println("# memberId: " + memberId);
        //리턴 값을 변경된 ResponseEntity로 대체
        //HttpStatus.OK 는 200, OK를 의미한다
        return new ResponseEntity<>(HttpStatus.OK);
        //not implementation
//        return null;
    }

    @GetMapping //별도의 URI를 지정해주지 않았기 때문에 클래스 레벨의 URI(“/v1/members”)에 매핑된다

    //getMembers() 메서드는 회원 목록을 클라이언트에게 제공하는 핸들러 메서드이다
    public /*String*/ResponseEntity getMembers() {
        System.out.println("# get Members");
        //리턴 값을 변경된 ResponseEntity로 대체
        //HttpStatus.OK 는 200, OK를 의미한다
        return new ResponseEntity<>(HttpStatus.OK);
        //not implementation
//        return null;
    }
}
  • 위의 코드를 실행하면 error가 발생한다
  • Member class를 생성하라는 메시지가 확인된다
  • new Member에 대한 신규 class를 생성해 주면 해결된다
package com.dreamfactory.exam_controller.member;

public class  Member {
    public Member(String email, String name, String phone) {
    }
}
  • postman으로 get/post를 실행하면 아래와 같은 CLI 메시지가 출력된다

 

 3) HttpServletRequest 객체로 헤더 정보 얻기

  • HttpServletRequest 객체를 이용하면 Request 헤더 정보에 다양한 방법으로 접근이 가능하다
  • HttpServletRequest는 다양한 API를 지원하지만 특정 헤더 정보에 접근하고자 한다면 @RequestHeader 가 더 용이하다
  • orderController에 HttpservletRequest 적용하기
package com.dreamfactory.exam_controller.order;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping(value="v1/orders"/*, produces = MediaType.APPLICATION_JSON_VALUE*/)

public class OrderController {
    @PostMapping

    //postOrder() 메서드는 커피 주문 정보를 등록한다
    public /*String*/ResponseEntity postOrder(HttpServletRequest httpServletRequest,
                                              @RequestParam("memberId") String memberId,
                                              @RequestParam("coffeeId") String coffeeId) {

////        Map<String, String> map = new HashMap<>();
////        map.put("memberId", memberId);
////        map.put("coffeeId", coffeeId);
//
//        return new ResponseEntity(map, HttpStatus.CREATED);

        System.out.println("user-agent: " + httpServletRequest.getHeader("user-agent"));
        return new ResponseEntity<>(new Order(memberId, coffeeId),
                HttpStatus.CREATED);
//        System.out.println("# memberId:" + memberId);
//        System.out.println("# coffeeId:" + coffeeId);
//
//        String reponse =
//                "{\"" +
//                        "memberId\":\"" + memberId + "\"," +
//                        "\"coffeeId\":\"" + coffeeId +
//                        "\"}";
//        return reponse;
    }

    @GetMapping("/{order-id}")
    public /*String*/ResponseEntity getOrder(@PathVariable("order-id") long orderId) {
        System.out.println("# orderId: " + orderId);
        return new ResponseEntity(HttpStatus.OK);
    }

    @GetMapping //별도의 URI를 지정해주지 않았기 때문에 클래스 레벨의 URI(“/v1/orders”)에 매핑된다

    //getOrders() 메서드는 주문 목록을 클라이언트에게 제공하는 핸들러 메서드이다
    public /*String*/ResponseEntity getOrders() {
        System.out.println("# get Orders");
        return new ResponseEntity(HttpStatus.OK);
    }
}
  • 실행하면 error가 발생한다
  • new Order에 대한 신규 class를 생성하면 해결된다
package com.dreamfactory.exam_controller.order;

public class Order {
    public  Order (String memberId, String CoffeeId){

    };
}
  • postman으로 post/get를 실행하면 아래와 같이 CLI가 출력된다

 

 

 

※ 참조 링크

▶ HTTP Header : https://developer.mozilla.org/ko/docs/Web/HTTP/Headers

 

HTTP 헤더 - HTTP | MDN

HTTP 헤더는 클라이언트와 서버가 요청 또는 응답으로 부가적인 정보를 전송할 수 있도록 해줍니다. HTTP 헤더는 대소문자를 구분하지 않는 이름과 콜론 ':' 다음에 오는 값(줄 바꿈 없이)으로 이루

developer.mozilla.org

 HttpServletRequest : https://docs.oracle.com/javaee/7/api/index.html?javax/servlet/http/HttpServletRequest.html 

 

Java(TM) EE 7 Specification APIs

 

docs.oracle.com

https://docs.oracle.com/javaee/7/api/index.html?javax/servlet/http/HttpServletResponse.html 

 

Java(TM) EE 7 Specification APIs

 

docs.oracle.com

User Agent의 유형 : https://gist.github.com/pzb/b4b6f57144aea7827ae4

 

user-agents.txt

GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent

 

User-Agent - HTTP | MDN

The User-Agent request header is a characteristic string that lets servers and network peers identify the application, operating system, vendor, and/or version of the requesting user agent.

developer.mozilla.org

▶ 백엔드 서비스에 커스텀 헤더 만들기 : https://cloud.google.com/load-balancing/docs/https/custom-headers?hl=ko 

 

백엔드 서비스에 커스텀 헤더 만들기  |  부하 분산  |  Google Cloud

전역 외부 HTTP(S) 부하 분산기(기본)에서 사용하는 백엔드 서비스의 커스텀 헤더를 구성합니다.

cloud.google.com

HTTP 헤더 및 Application Load Balancer : https://docs.aws.amazon.com/ko_kr/elasticloadbalancing/latest/application/x-forwarded-headers.html

 

HTTP 헤더 및 Application Load Balancer - Elastic Load Balancing

HTTP 요청 및 HTTP 응답은 헤더 필드를 사용하여 HTTP 메시지에 대한 정보를 전송합니다. HTTP 헤더가 자동으로 추가됩니다. 헤더 필드는 콜론으로 구분된 이름-값 페어이며 CR(캐리지 리턴) 및 LF(줄

docs.aws.amazon.com

 

+ Recent posts