본문 바로가기
[ java ]

[Spring] JSON / JSONP 개념 / spring MVC 복습(2)

by 히앤님 2020. 10. 27.
반응형
SMALL

JSONP

보안상의 이슈를 우회하여 서로 다른 도메인이 데이터 공유가 가능하게 방법.

(JSONP는 여러 보안상 이슈로 인하여 W3C에서는 2009년 채택된 CORS 방식의 HTTP 통신을 권장해서 이젠 거의 사용하지는 않는다.)

 

 

 

정확하게 이해하기 위해서는 차례대로 스토리가 필요하다.

JSON(JavaScript Object Notation)

일반적으로 서버에서 클라이언트로 데이터를 보낼 때 사용하는 양식. 클라이언트가 사용하는 언어에 관계 없이 통일된 데이터를 주고받을 수 있도록, 일정한 패턴을 지닌 문자열을 생성해 내보내면 클라이언트는 그를 해석해 데이터를 자기만의 방식으로 온전히 저장, 표시할 수 있게 된다.

과거 웹 초기 시절부터 사용되어 온 XML은 헤더와 태그 등의 여러 요소로 가독성이 떨어지고, 쓸데없이 용량을 잡아먹는다는 단점이 항상 지적되어 왔다. 이에 대응해 간결하고 통일된 양식으로 각광을 받고 있는 것이 JSON이다.

[]안의 것은 순서가 있는 배열, {}안의 것은 속성명(흔히 이름표라고 한다)이 있는 객체를 의미하며, 객체 안에 객체를 넣을 수도 있어서 XML처럼 복잡한 구조 또한 표현이 가능하다.

기본적으로 JavaScript 객체 표기법의 부분집합으로, Ajax 구현에도 많이 쓰인다.

▼JSON 장점

더보기
더보기

 C,C++,C#,자바,자바스크립트, 펄, 파이썬 등 많은 프로그래밍 언어에서 동종 또는 이기종 S/W끼리 쉽게 데이터를 전달할 수 있다. (원래 제이슨 전에는 XML 사용했었다. XML 대체 가능. XML은 현재 환경설정 파일에서 많이 사용하고 있다.)

1. 이기종 SW 끼리 디비연동 데이터 주고받을 때, 2. Spring에서 데이터 주고받을 때) 
2.웹브라우저에서 비동기 방식으로 서버와 클라이언트 끼리 쉽게 데이터를 주고받을 수 있다. 
  <예>웹상에서 비동기 방식으로 클라이언트가 서버로 요청을 할 경우 서버가 json파일로 응답할 때 
3.비동기 방식
 ㄱ.클라이언트가 페이지 이동 없이 AJAX기술로 서버와 통신하는 방식을 말한다. 
 ㄴ.<예>아이디 입력 후 버튼을 눌러 페이지 전환 없이 서버와 통신하여 아이디 중복여부를 수신받는경우 
 ㄷ.<예>네이버에서 검색 키워드 입력 시 페이지 전환 없이 서버와 통신하여 관련 검색어가 밑에 뜨는 경우 
4.XML을 대체할 수 있다. 

# 비동기 방식을 구현하는데 사용되는 기술들의 총칭 : AJAX(비동기식 자바스크립트 XML(Asynchronous Javascript And XML)의 약자) 

 


 

AJAX(Asynchronous JavaScript and XML)

JavaScript와 XML을 이용한 비동기적 정보 교환 기법.

원래 Ajax 전에는 웹브라우저가 정보를 요청하면 서버가 그 정보가 담긴 전체 페이지를 보내주었다. 정보를 받으면 처음부터 다시 로딩하고 만들어야 했기 때문에 느리고 버벅거렸다.

Ajax는 웹페이지의 디자인 요소와 정보 요소를 분리해서(HTML과 CSS의 분리), 웹브라우저에게 정보요소만 어떻게 요청하는지를 설명한 javascript 파일을 전달한다. 웹브라우저가 그 기반으로 정보를 요청하면 서버는 그 데이터를 순수한 데이터 객체(XML 또는 JSON 형태)로 준다. 웹브라우저는 받은 데이터를 적절한 방법으로 끼워넣는다.

이러한 장점 때문에 웹 환경에서 HTML과 javascript가 연동되는 환경에서 주로 사용된다. 

 

JSON과 Ajax의 단점은 XML이나 다른 데이터 형식에 비해 문법 오류에 민감하다는 것이다.

주석을 지원하지 않고, 데이터 형식을 지정할 수 없기 때문에 있는 그대로 데이터 전달하는 역할에 주로 쓰인다.

 

CORS

도메인(예를들면 www.naver.com)이 서로 다른 곳끼리의 통신을 위한 규약.

 

원래 이 규약이 생기기 전에 javascript에서 서로 다른 도메인이 요청을 하면 보안상 제한했었다.

Ajax의 경우 다른 도메인 서버의 자원을 요청할 경우 아래와 같은 메시지가 떴었다.

"교차 원본 요청 차단: 동일 출처 정책으로 인해 'www.b.com'에 있는 원격 자원을 읽을 수 없습니다. 자원을 같은 도메인으로 이동시키거나 CORS를 활성화하여 해결할 수 있습니다."

외부로 요청이 안 되는 것은 자바스크립트 엔진 표준 스펙에 동일 출처 정책(same-origin policy)이라는 보안 규칙이 존재했기 때문인데 이를 SOP라고 한다. 즉, 웹 환경에서 img나 css 파일은 가져올 수 있으나 보안상 <script>로 둘러싸인 데이터(=Cross-Site HTTP Requests)는 접근이 불가능하다.

SOP에서는 프로토콜, 호스트명, 포트번호가 같아야만 자원 공유가 가능하다.

 

그래서 ajax를 이용하기 위해서는 SOP를 부수고 CORS를 허용해주어야 한다. CORS는 웹브라우저가 외부 도메인 서버와 통신하기 위한 방식을 표준화 한 것으로, 서버와 클라이언트가 정해진 헤더를 통해 어떻게 서로 요청하고 응답할건지를 정해놓은 것이다.

 

그러한 방식중에 하나가 JSONP 이다.

 

JSONP(JSON with Padding)

그러므로 jsonp은 데이터 타입을 요청하는 것이 아닌 HTML의 <script></script>태그를 json 형식으로 변경 후 읽어오는 방법이다. 단, GET 방식만 요청이 가능하다.

여기서 script 요소는 src를 호출한 결과를 javascript를 불러와서 포함시키는 것이 아니고 실행시키는 태그이다. 

 

예를들어 어떤 도메인 주소/a를 하면 a에 해당하는 json 데이터를 준다고 가정하자.

<script src="~~/a">를 통해 데이터를 받아왔어도, 이 javascript를 실행만 한거지, 변수를 지정해서 저장 후 가져온 것이 아니기 때문에 접근이 불가하다. 따라서 이 json 데이터를 가져오기 위해 콜백함수를 사용한다.

 src 속성의 값(주소값) 뒤에 ?callback=콜백함수명을 붙여서 요청한다. 그러면 요청의 답으로 콜백함수 인자에 이 객체를 전달해서 실행시켜준다. (데이터가 열린다)

데이터를 전송한 페이지를 열면 에러가 뜨고, 그 도메인에  ?callback=콜백함수명 을 붙여줘야한다!

 

= > 즉, JSONP를 사용하기 위해서는 서버에서 데이터를 반환할때 콜백함수를 감싸주는 처리가 필요하다.

출처 : 1, 2, 3


우선 JSON 연습부터 해보자.

 

[1] 데이터를 그대로 던져주는 방법(custom view)

[문자열 던져주기]

1. 컨트롤러 만들기

연습 할 컨트롤러 생성

 

2. RequestBody사용해서 데이터 그대로 출력하기

저번 포스팅에서 root-context.xml에 viewResolver를 설정해주었다. 따라서 return 뒤에 오는 값인 jsp 파일을 view 폴더에서 찾아서 페이지 이동을 시켜야하는데, 지금 hi.jsp가 없다.

 

클라이언트에서 서버로 필요한 데이터를 전송하기 위해서 JSON이라는 데이터를 요청 본문에 담아서 서버로 보내면, 서버에서는 @RequestBody 어노테이션을 사용하여 HTTP 요청 본문에 담긴 값들을 자바 객체로 변환 시켜, 객체에 저장시킨다.

 

@RequestBody를 쓰면 viewResolver 설정한 것이 아닌 그냥 hi 라는 문자열이 출력된다.

custom view 를 사용한 것인데, 쉽게 말해 개발자가 직접 만든 뷰. 사용자 정의 뷰이다. 주로 json 형식을 만들 때 많이 사용한다.

여기서는 @RequestMapping 으로 jsonpTest1에 URL을 매핑해주고, @RequestBody를 이용해 요청값을 객체에 저장시킨다. 그렇게 새로 만든 testjson이 hi라는 문자열을 출력할 수 있게 해준다.

왜?

 

@Controller
public class JsonpController {

	//custom view => json 형식을 만들 때 많이 사용한다.
	@RequestMapping(value="/jsonpTest1",method = RequestMethod.GET)
	@ResponseBody 
	public String testjson() {
		return "hi";
	}	

 

 

3. index에 연결

2번에서 매핑한 jsonpTest1로 연결해주면

<li><a href="jsonpTest1">jsonTest</a></li>

결과

[list 던져주기]

4. 동일한 방법으로 listv 값을 가져오자.

listv는 이것들이다.

package kr.co.ikosmo.mvc.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import kr.co.ikosmo.mvc.dao.BoardDao;
import kr.co.ikosmo.mvc.vo.BoardVO;

@Controller
public class JsonpController {
	
	@Autowired
	private BoardDao dao;
	
	//custom view => json 형식을 만들 때 많이 사용한다.
	@RequestMapping(value="/jsonpTest1",method = RequestMethod.GET)
	@ResponseBody 
	public String testjson() {
		return "hi";
	}	
	
	//spring 4x 제공, PostMapping
	@GetMapping(value = "/jsonpTest2")
	@ResponseBody 
	public List<BoardVO> boardList(Model m) {
		List<BoardVO> listv = dao.getBoardList();
		m.addAttribute("listv", listv);
		return listv;
	}	
		
	
}

 

위에 경우와 마찬가지로 listv는 원래였으면 view 폴더 안에 있는 jsp 파일이었어야 한다.

하지만 에러가 나는 대신, listv라는 객체를 찾아내어 boardVO에 선언된 변수들을, boardList.jsp에서 연결된 오라클에 저장된 그 데이터 전부를 가져온다. 데이터를 그대로 던져준 것.

 

5. index에 연결

 

	<li><a href="jsonpTest2">jsonListTest</a></li>

결과를 보면,

DB 데이터가 제이슨 형태로 출력되었다.

 

 

이제 위에서 한거 + jsonp를 합쳐보자.

 

 

1. jsonpTest3 입력

callback 함수를 사용해서 데이터를 json 형식으로 받아온다.

@RequestMapping(value = "/jsonpTest3", method = RequestMethod.GET)
   @ResponseBody
   // URL 요청시 ID 값과 callback 값을 인자로 전달 받는다.
   public String jaonpTest(@RequestParam String callback) {
      Map<String, Double[]> paramMap = new HashMap<String, Double[]>();
      Double[] a1 = { 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 
            0.1, 0.1, 0.2, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.4, 0.2, 0.5, 0.2, 0.2, 
            0.4, 0.2, 0.2, 0.2, 0.2, 0.4, 0.1, 0.2, 0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 
            0.3, 0.3, 0.2, 0.6, 0.4, 0.3, 0.2, 0.2, 0.2, 0.2 };
      Double[] a2 = { 1.4, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6, 1.0, 1.3, 1.4, 1.0, 1.5, 1.0, 
            1.4, 1.3, 1.4, 1.5, 1.0, 1.5, 1.1, 1.8, 1.3, 1.5, 1.2, 1.3, 1.4, 1.4, 1.7, 
            1.5, 1.0, 1.1, 1.0, 1.2, 1.6, 1.5, 1.6, 1.5, 1.3, 1.3, 1.3, 1.2, 1.4, 1.2, 
            1.0, 1.3, 1.2, 1.3, 1.3, 1.1, 1.3 };
      Double[] a3 = { 2.5, 1.9, 2.1, 1.8, 2.2, 2.1, 1.7, 1.8, 1.8, 2.5, 2.0, 1.9, 2.1, 2.0, 
            2.4, 2.3, 1.8, 2.2, 2.3, 1.5, 2.3, 2.0, 2.0, 1.8, 2.1, 1.8, 1.8, 1.8, 2.1, 1.6, 
            1.9, 2.0, 2.2, 1.5, 1.4, 2.3, 2.4, 1.8, 1.8, 2.1, 2.4, 2.3, 1.9, 2.3, 2.5, 2.3, 
            1.9, 2.0, 2.3, 1.8 };
      paramMap.put("setosa", a1);
      paramMap.put("versicolor", a2);
      paramMap.put("virginica", a3);
      String result = null;
      ObjectMapper mapper = new ObjectMapper();
      try {
         result = mapper.writeValueAsString(paramMap);
      } catch (JsonMappingException e) {
         e.printStackTrace();
      } catch (JsonGenerationException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }
      // Jsonp 형식으로 데이터를 제공한다.
      System.out.println(result);
      return callback + " ( " + result + " ) ";
   }

 

2. index.jsp에 추가

	<li><a href="jsonpTest3">jsonpTest3</a></li>

 

3. 결과

실행하면 400 에러가 뜬다. 데이터를 callback하려면 src 뒤에 ?callback=콜백함수명 을 써줘야 한다.

400 에러

 

결과


다음 포스팅에서 JSONP를 이용해서 디장고와 Spirng mvc 연동해보자.

반응형
LIST

댓글