본문 바로가기
Lecture/Spring

[본격 게시판짜기 Part3.7 Spring MVC] JSON 요청이란?

by cusmaker 2012. 10. 29.
반응형

2012/06/13 - [Lecture/HTML] - [본격 게시판짜기 Part1.1 - 게시판도 HTML부터] 게시글 리스트

2012/06/13 - [Lecture/HTML] - [본격 게시판짜기 Part1.2 - 게시판도 HTML부터] 글입력폼

2012/06/13 - [Lecture/Javascript-기초] - [본격 게시판짜기 Part1.3 - Dom 맛보기 ] 글입력폼 검사

2012/06/13 - [Lecture/Jsp] - [본격 게시판짜기 Part1.4 - HTML-> JSP] form 파라미터 받기

2012/06/26 - [Lecture/SQL / Oracle] - [본격 게시판짜기 Part1.5 JSP > Oracle] 게시판 DB 테이블 생성

2012/06/26 - [Lecture/Jsp] - [본격 게시판짜기 Part1.6 Oracle > JSP] Database 접속 및 Select

2012/07/05 - [Lecture/Jsp] - [본격 게시판짜기 Part1.7 JSP, SQLDeveloper] 게시글 입력 및 리스트조회기능

2012/07/08 - [Lecture/Jsp] - [본격 게시판짜기 Part1.8 JSP 게시글 조회] 게시글 조회기능 및 게시글 삭제

2012/07/10 - [Lecture/Jsp] - [본격 게시판짜기 Part2.1 Model2 MVC패턴] 뷰(View) 코드 분리

2012/07/10 - [Lecture/Jsp] - [본격 게시판짜기 Part2.2 Model2 MVC패턴] Entity Beans의 사용

2012/07/12 - [Lecture/Jsp] - [본격 게시판짜기 Part2.3 Model2 MVC패턴] Controller구성

2012/07/13 - [Lecture/Jsp] - [본격 게시판짜기 Part2.4 Model2 MVC패턴] Model 구성

2012/07/16 - [Lecture/Mybatis] - [본격 게시판짜기 Part2.5 Model2 MVC패턴] Model 구성2 - i-batis의 사용

2012/07/17 - [Lecture/Mybatis] - [본격 게시판짜기 Part2.6 Model2 MVC패턴] Model 구성2 - i-batis의 사용2

2012/07/21 - [Lecture/Mybatis] - [본격 게시판짜기 Part2.7 Model2 MVC패턴] i-batis의 queryForList 활용 Paging기능구현

2012/07/22 - [Lecture/Jsp] - [본격 게시판짜기 Part2.8 Model2 MVC패턴] count기능추가

2012/07/28 - [Lecture/Jsp] - [본격 게시판짜기 Part2.9 Model2 MVC패턴] 파일업로드 기능추가

2012/07/28 - [Lecture/Jsp] - [본격 게시판짜기 Part2.10 Model2 MVC패턴] 파일다운로드/삭제 기능추가

2012/07/31 - [Lecture/Jsp] - [본격 게시판짜기 Part3.1 Spring MVC] Ajax란?

2012/08/28 - [Lecture/Jsp] - [본격 게시판짜기 Part3.2 Spring MVC] lightbox 적용하기

2012/09/22 - [Lecture/Spring] - [본격 게시판짜기 Part3.3 Spring MVC] Spring MVC 개요 및 설정하기

2012/09/22 - [Lecture/Spring] - [본격 게시판짜기 Part3.4 Spring MVC] Spring MVC 개요 및 설정하기2

2012/10/13 - [Lecture/Spring] - [본격 게시판짜기 Part3.5 Spring MVC] SqlMapClientDaoSupport활용, Model 변경하기

2012/10/29 - [Lecture/Spring] - [본격 게시판짜기 Part3.6 Spring MVC] multipartResolver를 활용한 파일업로드 변경하기


안녕하세요 cocy입니다.

이번시간에는 게시글 삭제 기능과 다운로드를 스프링 스타일로 전환하고,

JSON 요청에 대해 알아보도록 하겠습니다.



JSON에 대한 간략한 정의를 담아보았습니다.

간단히 말해 사용하기 쉽도록 만들어진 데이터셋 정도로 이해하시면 되겠습니다.

이미 저희는 스프링설정을 통해 각 요청별로 .json을 붙여 요청을 하면 

스프링에서 지원하는 ContentNegotiatingViewResolver 를 통해 json형태의 데이터들을 받을 수가 있습니다.

한번 http://localhost:8080/board/list/0.json 의 형태로 주소창에 직접 요청을 날려보시기바랍니다.

신기하죠? 

일반적으로는 json요청으로 응답을 받기위해서는 일일히 json 스트링을 통해 파싱하는 작업을 거쳐야하는데,

그럴 필요없이 단지 요청을 하면 json형태로 반환합니다. ModelAndView객체와 ContentNegotiatingViewResolver의 합작입니다.

그럼 이것을 어떻게 활용하느냐!

json형태의 데이터들은 자바스크립트에서 사용하기 아주 간편하며 특히 비동기 요청에서 많이 쓰입니다.


이번에 진행할 내용이 게시글 삭제전환인데요, 

json요청을 통해 게시글 삭제를 화면의 전환없이 비동기로 처리하고 결과값을 json으로 받아

처리 성공시 lightbox를 닫고 리스트페이지를 새로고치는 방식으로 구현해보도록 하겠습니다.


우선 먼저 게시글 삭제를 위해 content.jsp페이지를 보겠습니다.

<a href="delete.do?idx=${article.idx}">게시글삭제</a>

이런 기존 구문에 자바스크립트 함수를 걸고

function deleteArticle(idx){

$.ajax({

  type:'post',

  url:'/board/delete.json',

  data: ({idx:idx}),

  success:function(data){

  console.log(data);    // 브라우저 콘솔에 표시

 if(data.status == "success"){

 alert("삭제되었습니다.");

 location.reload();

 }

  }

});

}

.

.

<a href="#" onclick="deleteArticle('${article.idx}')">게시글삭제</a>

url을 rest-ful 형식으로 바꿔주면서 .json을 붙여 json요청을 합니다.


그리고 또 BoardController.java(spring.board.controller)로 가서 해당 url을 처리할 메소드 정형을 선언합시다.

 @RequestMapping("/delete") 

 public abstract ModelAndView deleteAction(@RequestParam Integer idx, HttpServletRequest request) throws Exception;  

다음은 구현을 해주어야겠죠? BoardService.java(spring.board.service)

@Override

public ModelAndView deleteAction(Integer idx, HttpServletRequest request) throws Exception {

Board article = boardDao.getArticle(idx);

String filename = article.getFilename();

String uploadFileName = request.getRealPath("/upload") +"/"+ filename;


File uploadfile = new File (uploadFileName);

if ( uploadfile.exists()&& uploadfile.isFile() )

{

uploadfile.delete(); // 파일 삭제

}

boardDao.deleteArticle(idx);

return new ModelAndView("json", "status", "success");

}

여기서 뷰이름을 json으로 리턴하는데

json요청에서는 데이터만을 받기때문에 페이지가 필요가 없습니다.

허나 null 포인터 에러를 방지하기위해 아무것도 없는 빈페이지(json.jsp)를 WebContent>WEB-INF > board 폴더 밑에 생성하시고

template-board.xml (spring.board.view) 에서 다음과 같이 추가해주시기 바랍니다.

<definition name="json" template="/WEB-INF/board/json.jsp"/>


그럼 기능이 정상적으로 작동하는지 확인해볼까요?

그전에 콘솔에 json데이터가 정상적으로 받아와 뿌려졌는지 확인하기위해 location.reload(); 구문에 주석처리를 하겠습니다.



사진에 화면은 크롬 브라우저로 실행후 f12를 누르고 콘솔탭에서 확인한 결과입니다.

코드인스펙터라는 기능인데 자세한 내용은 검색해 보시기바라며,

보시는 바와같이 정상적으로 json데이터가 넘어오고 ModelAndView 객체에서 반환한

status와 success라는 값이 들어있는 것을 확인 하실 수 있습니다.



그럼 마지막으로 다운로드 기능을 구현해보도록 하겠습니다.

다운로드에서도 마찬가지로 스프링에서 지원하는 빈네임 뷰 리졸버를 사용하는데요,

이것은 다운로드에 특화되었다기 보다는 클래스를 뷰네임으로 반환하여 뷰 페이지 대신 사용하는 것입니다.


예를 들어 파일 다운로드에 사용될 클래스를 지정하고 해당 클래스이름에 

가장앞글자를 소문자로 치환하여 (ex - DownloadView > downloadView)  ModelAndView객체의 첫번째 인자로 전달하면

해당 클래스가 실행되는 원리입니다.



보실부분은 다시 content.jsp입니다.

function onDownload(idx) {

var o = document.getElementById("ifrm_filedown");

o.src = "download.do?idx="+idx;

}

스프링만의 파일다운로드 뷰를 구현하기때문에 이 부분은 꼼수를 부릴 필요가 없어집니다.

지워주시고 파일 다운로드 하는링크 부분을 다음과같이 변경해줍니다.

<td colspan="8"><a href="#" onclick="onDownload('${article.idx}')">${article.filename}</a></td>

<td colspan="8"><a href="/board/download/${article.idx}" >${article.filename}</a></td>

마찬가지로 rest-ful 형식으로 바꿔줍니다. 이번에는 @PathVariable을 사용합니다.

그리고 또 BoardController.java(spring.board.controller)로 가서 해당 url을 처리할 메소드 정형을 선언합시다.

@RequestMapping("/download/{idx}")

public abstract ModelAndView downloadAction(@PathVariable Integer idx, HttpServletRequest request) throws Exception;

다음은 구현을 해주어야겠죠? BoardService.java(spring.board.service)

@Override

public ModelAndView downloadAction(@PathVariable Integer idx, HttpServletRequest request)

throws Exception {

Board article = boardDao.getArticle(idx);

String filename = article.getFilename();

String uploadFileName = request.getRealPath("/upload") +"/"+ filename;

File downFile = new File(uploadFileName);

return new ModelAndView("downloadView", "file", downFile);

}

기존에 다운로드액션의 소스에 비해 무언가 많이 빈약합니다.

단지 파일을 생성한후 downloadView라는 뷰에 파일을 넘기고 있습니다.

이것이 바로 위에서 설명한 빈 네임 뷰 리졸버를 활용한 예입니다.

DownloadView.java라는 파일을 추가해주고 다운로드에 관한 내용을 모두 위임하였습니다.

해당파일은 뷰이기때문에 spring.board.view 패키지에 추가하였습니다. 내용을 훑어보겠습니다.

package spring.board.view;

import java.io.File;

import java.io.FileInputStream;

import java.io.OutputStream;

import java.net.URLEncoder;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;

import org.springframework.util.FileCopyUtils;

import org.springframework.web.servlet.view.AbstractView;


@Component        // 스프링 컨테이너에 등록하기 위해 컴포넌트 어노테이션을 사용합니다.

public class DownloadView extends AbstractView {    // 클래스 이름인 DownloadView의 첫글자가 소문자에 해당하는

public void Download() {                                // downloadView를 뷰네임으로 리턴시 이 클래스가 실행됩니다

setContentType("application/download; utf-8");        //컨텐츠 타입을 정의합니다. 인코딩은 utf-8

}

@Override

protected void renderMergedOutputModel(Map<String, Object> model,

HttpServletRequest request, HttpServletResponse response)

throws Exception {


File file = (File) model.get("file");        // ModelAndView객체로 부터 파일을 받아서


if(file.exists() && file.isFile()){ // 파일이 존재하면 다운로드합니다.

response.setContentType(getContentType());

response.setContentLength((int) file.length());

String userAgent = request.getHeader("User-Agent");

boolean ie = userAgent.indexOf("MSIE") > -1;

String fileName = null;

if (ie) {

fileName = URLEncoder.encode(file.getName(), "utf-8");

} else {

fileName = new String(file.getName().getBytes("utf-8"));

}// end if;

response.setHeader("Content-Disposition", "attachment; filename=\""

+ fileName + "\";");

response.setHeader("Content-Transfer-Encoding", "binary");

OutputStream out = response.getOutputStream();

FileInputStream fis = null;

try {

fis = new FileInputStream(file);

FileCopyUtils.copy(fis, out);

} catch (Exception e) {

e.printStackTrace();

} finally {

if (fis != null) {

try {

fis.close();

} catch (Exception e) {

}

}

}// try end;

out.flush();

}// render() end;

}

}

기존의 다운로드 뷰와 흡사한 구조를 가지고 있습니다.

컨텐츠 타입을 정의하고 파일을 다운로드합니다.

다운로드 뷰에서는 기본적으로 스프링에서 제공하는 AbstractView를 상속받아 renderMergedOutputModel을 구현합니다.


이상으로 포스팅을 마칩니다.

수고하셨습니다.


다음장 (마지막) rest-ful 서비스에서 정적 자원 사용 및 디자인 적용하기 >> http://cusmaker.tistory.com/137