728x90
300x250

[Spring-Framework] 20. Spring MVC - Spring Framework 5 REST, Jackson, Commons-FileUpload - (2)


2부에서는 Controller, Model, View, Util(한글 문제)을 집중적으로 소개하겠다.


1. [Spring-Framework] 19. Spring MVC - Spring Framework 5 REST, Jackson, Commons-FileUpload - (1), 2020-09-28

https://yyman.tistory.com/1425



12. Controller - HomeController.java



초기 프로젝트를 생성하면 자동으로 만들어지는 HomeController.java이다.

REST 프로젝트 작업에 필요한 형태로 추가 작성 및 변형하였다.


- REST Client에 대해서 자세히 소개하였음. (CRUD - GET, POST, PUT, DELETE 클라이언트)


package com.example.restexample2.controller;


import java.io.UnsupportedEncodingException;

import java.net.URI;

import java.net.URLDecoder;

import java.nio.charset.Charset;

import java.text.DateFormat;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Date;

import java.util.HashMap;

import java.util.List;

import java.util.Locale;

import java.util.Map;

import java.util.concurrent.atomic.AtomicLong;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.http.HttpEntity;

import org.springframework.http.HttpHeaders;

import org.springframework.http.HttpMethod;

import org.springframework.http.MediaType;

import org.springframework.http.ResponseEntity;

import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

import org.springframework.http.converter.FormHttpMessageConverter;

import org.springframework.http.converter.HttpMessageConverter;

import org.springframework.http.converter.StringHttpMessageConverter;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.util.LinkedMultiValueMap;

import org.springframework.util.MultiValueMap;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

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

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.client.HttpClientErrorException;

import org.springframework.web.client.HttpServerErrorException;

import org.springframework.web.client.RestTemplate;

import org.springframework.web.util.UriComponents;

import org.springframework.web.util.UriComponentsBuilder;


import com.example.restexample2.model.Board;

import com.example.restexample2.model.Greeting;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.databind.deser.impl.CreatorCandidate.Param;


@Controller

public class HomeController {

private static final Logger logger = LoggerFactory.getLogger(HomeController.class);;

@RequestMapping(value = "/", method = RequestMethod.GET)

public String home(Locale locale, Model model) {

logger.info("Welcome home! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

model.addAttribute("serverTime", formattedDate );

return "home";

}


    /**

     * 파일 업로드 입력 화면

     */

@RequestMapping(value = "/fileUploadView", method = RequestMethod.GET)

public String fileUploadView(Locale locale, Model model) {

logger.info("Welcome FileUpload! The client locale is {}.", locale);

Date date = new Date();

DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

String formattedDate = dateFormat.format(date);

model.addAttribute("serverTime", formattedDate );

return "file/upload";

}



    /**

     * GET 방식 - 클라이언트

     */

@SuppressWarnings("unchecked")

@RequestMapping(value = "/client/listMapGet", method = RequestMethod.GET)

public String helloClient(String regId, String time) throws UnsupportedEncodingException{

// Get 응답 방법론

String url = "http://localhost:8080/restexample2/testValue2";

        String serviceKey = "서비스키";

        String decodeServiceKey = URLDecoder.decode(serviceKey, "UTF-8");

        

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();

        headers.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));    //Response Header to UTF-8  

        

        UriComponents builder = UriComponentsBuilder.fromHttpUrl(url)

                .queryParam("serviceKey", decodeServiceKey)

                .queryParam("regId", regId)

                .queryParam("tmFc", time)

                .queryParam("_type", "json")

                .build(false);    //자동으로 encode해주는 것을 막기 위해 false

        

        //Object response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, new HttpEntity<String>(headers), String.class);

        

        // Object response = restTemplate.getForEntity(builder.toUriString(), List.class);

        Object response = restTemplate.getForObject(builder.toUriString(), List.class);

        

        if ( response != null) {


            List<Integer> map = (List<Integer>) response;

        System.out.println("map" + map);

        }

        

        //return response;


return "home";

}


    /**

     * POST 방식 - 클라이언트

     */

@SuppressWarnings("unchecked")

@RequestMapping(value = "/client/listMapPost")

public String helloClient2(String regId, String time) throws UnsupportedEncodingException{

// POST 응답 방법

//String url = "http://..............";

String url = "http://localhost:8080/restexample2/testValue2";

        String serviceKey = "서비스키";

        String decodeServiceKey = URLDecoder.decode(serviceKey, "UTF-8");

        

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();

        headers.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));    //Response Header to UTF-8  

        

        /*

        UriComponents builder = UriComponentsBuilder.fromHttpUrl(url)

                .queryParam("serviceKey", decodeServiceKey)

                .queryParam("regId", regId)

                .queryParam("tmFc", time)

                .queryParam("_type", "json")

                .build(false);    //자동으로 encode해주는 것을 막기 위해 false

        */

        

        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();

        parameters.add("servicekey", decodeServiceKey);

        parameters.add("regId", regId);

        parameters.add("tmFc", time);

        parameters.add("_type", "json");

        

        //Object response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, new HttpEntity<String>(headers), String.class);

        

        // Object response = restTemplate.getForEntity(builder.toUriString(), List.class);

        Object response = restTemplate.postForObject(url, parameters, List.class);

        // Object response = restTemplate.postForEntity(url, parameters, List.class);

        

        if ( response != null) {


            List<Integer> map = (List<Integer>) response;

        System.out.println("map" + map.get(0));

        }

        

        //return response;


return "home";

}

    /**

     * PUT 방식 - 클라이언트

     */

@SuppressWarnings("unchecked")

@RequestMapping(value = "/client/listMapPut/{boardIdx}")

public String helloClient3(String regId, 

String time,

@PathVariable(name="boardIdx", required=true) int boardIdx)

throws  UnsupportedEncodingException{

// PUT 방법

String url = "http://localhost:8080/restexample2/v1/api/board/2";

        String serviceKey = "서비스키";

        String decodeServiceKey = URLDecoder.decode(serviceKey, "UTF-8");

        

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();

        headers.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));    //Response Header to UTF-8  

        

        // 파라메터

        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();

        parameters.add("servicekey", decodeServiceKey);

        parameters.add("regId", regId);

        parameters.add("tmFc", time);

        parameters.add("_type", "json");

        

        //Object response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, new HttpEntity<String>(headers), String.class);

        

        Board updatedBoard = new Board();

        updatedBoard.setId(boardIdx);

        updatedBoard.setSubject("앙하하");

        updatedBoard.setName("수정했다.");

        updatedBoard.setMemo("메모지");

        

        

        // Object response = restTemplate.getForEntity(builder.toUriString(), List.class);

        //Object response = restTemplate.postForObject(url, parameters, List.class);

        restTemplate.put ( url, updatedBoard, parameters );

        

        // 데이터 확인하기

        url = "http://localhost:8080/restexample2/v1/api/board";


        UriComponents builder = UriComponentsBuilder.fromHttpUrl(url)

                .queryParam("serviceKey", decodeServiceKey)

                .build(false);


        Board[] response = restTemplate.getForObject(builder.toUriString(), Board[].class );

        // Object response = restTemplate.postForEntity(url, parameters, List.class);

        

        if ( response != null) {


            List<Board> listData = Arrays.asList(response);

        System.out.println("list(Size):" + listData.size());

       

        try {

        if ( listData.size() != 0 && (boardIdx - 1) >= 0) {

            System.out.println("boardIdx:" + (boardIdx - 1));

        Board boardTmp = listData.get(boardIdx - 1);

        System.out.println("board:" + boardTmp.getId() + "/" + boardTmp.getSubject());

        }

        }catch(Exception e) {

        e.printStackTrace();

        }

       

        }

        

        //return response;

return "home";

}


    /**

     * PUT 방식 - 클라이언트

     */

@SuppressWarnings("unchecked")

@RequestMapping(value = "/client/listMapDelete/{boardIdx}")

public String helloClient4(String regId, 

String time,

@PathVariable(name="boardIdx", required=true) int boardIdx)

throws  UnsupportedEncodingException{

   // PUT 방법

  String url = "http://localhost:8080/restexample2/v1/api/board/" + boardIdx;

        String serviceKey = "서비스키";

        String decodeServiceKey = URLDecoder.decode(serviceKey, "UTF-8");

        

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();

        headers.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));    //Response Header to UTF-8  

        

        // 파라메터

        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>();

        parameters.add("servicekey", decodeServiceKey);

        parameters.add("regId", regId);

        parameters.add("tmFc", time);

        parameters.add("_type", "json");

        

        //Object response = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, new HttpEntity<String>(headers), String.class);

        

        // Object response = restTemplate.getForEntity(builder.toUriString(), List.class);

        //Object response = restTemplate.postForObject(url, parameters, List.class);

        restTemplate.delete ( url, parameters );

        

        // 데이터 확인하기

        url = "http://localhost:8080/restexample2/v1/api/board";


        UriComponents builder = UriComponentsBuilder.fromHttpUrl(url)

                .queryParam("serviceKey", decodeServiceKey)

                .build(false);


        Board[] response = restTemplate.getForObject(builder.toUriString(), Board[].class );

        // Object response = restTemplate.postForEntity(url, parameters, List.class);

        

        if ( response != null) {


            List<Board> listData = Arrays.asList(response);

        System.out.println("list(Size):" + listData.size());

       

        try {

        if ( listData.size() != 0 && (boardIdx - 1) >= 0) {

            System.out.println("boardIdx:" + (boardIdx - 1));

        Board boardTmp = listData.get(boardIdx - 1);

        System.out.println("board:" + boardTmp.getId() + "/" + boardTmp.getSubject());

        }

        }catch(Exception e) {

        e.printStackTrace();

        }

       

        }

        

        //return response;

return "home";

}

}



파일명: HomeController.java


[첨부(Attachments)]

HomeController.zip





13. Controller - JSONController.java



REST를 간단하게 입문하는 용도로 작성하였다.


package com.example.restexample2.controller;


import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.concurrent.atomic.AtomicLong;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;


import com.example.restexample2.model.Greeting;


/**

 * Handles requests for the application home page.

 */

@RestController

public class JSONController {

private static final Logger logger = LoggerFactory.getLogger(JSONController.class);


private static final String template = "Hello, %s!";

private final AtomicLong counter = new AtomicLong();

@GetMapping("/greeting")

public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {

return new Greeting(counter.incrementAndGet(), String.format(template, name));

}

@GetMapping(value="/testValue") 

public String getTestValue(){

String TestValue = "레스트컨트롤러 테스트";

return TestValue;

}


@GetMapping(value="/testValue2") 

public List<Integer> getTestValue2(){

List<Integer> mList = new ArrayList<Integer>();

mList.add(1);

mList.add(2);

mList.add(3);

mList.add(4);

return mList;

/* 

* Error: pom.xml의 jackson-databind, jackson-core 추가할 것

* onverter found for return value of type: class java.util.ArrayList]

*/

}

@PostMapping(value="/testValue2") 

public List<Integer> getTestValue3(){

List<Integer> mList = new ArrayList<Integer>();

mList.add(1);

mList.add(2);

mList.add(3);

mList.add(4);

return mList;

/* 

* Error: pom.xml의 jackson-databind, jackson-core 추가할 것

* onverter found for return value of type: class java.util.ArrayList]

*/

}

@GetMapping(value="/getMap")

public Map<String, Greeting> getMap(){

Map<String, Greeting> map = new HashMap<>();

map.put("First", new Greeting(1, "Hello"));

map.put("Second", new Greeting(2, "Rest"));

return map;

}

}



파일명: JSONController.java


[첨부(Attachments)]

JSONController.zip




14. Model - Board.java



매우 간단한 게시판 구조에 대한 모델이다. 방명록도 어찌보면, 게시판의 한 종류가 될 수 있다.


package com.example.restexample2.model;


public class Board {

private long id;

private String subject;

private String name;

private String memo;

public long getId() {

return id;

}

public void setId(long id) {

this.id = id;

}

public String getSubject() {

return subject;

}

public void setSubject(String subject) {

this.subject = subject;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getMemo() {

return memo;

}

public void setMemo(String memo) {

this.memo = memo;

}

}



파일명: Board.java


[첨부(Attachments)]

Board.zip



15. Model - Greeting.java



사실 쉽고 간단한 코드가 더 어려운 코드라고 본다.

꼭 반드시 게시판 형태만 생각할 필요가 없다고 본다.

공부하는 데 있어서는 간단한 것이 사실 더 어려운 부분이라고 본다.


package com.example.restexample2.model;


public class Greeting {


private final long id;

private final String content;


public Greeting(long id, String content) {

this.id = id;

this.content = content;

}


public long getId() {

return id;

}


public String getContent() {

return content;

}

}


파일명: Greeting.java


[첨부(Attachments)]

Greeting.zip




16. Model - FileInfo.java


경로: /src/main/java/com/example/restexample2/model/FileInfo.java


파일에 관한 명세이다.


package com.example.restexample2.model;


import org.springframework.web.multipart.MultipartFile;


public class FileInfo {


private long num;

private String filename;

private long filesize;

private MultipartFile mediaFile;


public long getNum() {

return num;

}

public void setNum(long num) {

this.num = num;

}

public String getFilename() {

return filename;

}

public void setFilename(String filename) {

this.filename = filename;

}

public long getFilesize() {

return filesize;

}

public void setFilesize(long filesize) {

this.filesize = filesize;

}

public MultipartFile getMediaFile() {

return mediaFile;

}


public void setMediaFile(MultipartFile mediaFile) {

this.mediaFile = mediaFile;

}

}



파일명: FileInfo.java


[첨부(Attachments)]

FileInfo.zip





17. Controller - FileController.java


경로: /src/main/java/com/example/restexample2/controller/FileController.java


파일 업로드에 대한 기능이다.


package com.example.restexample2.controller;


import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;


import javax.servlet.ServletContext;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.HttpHeaders;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.ModelAttribute;

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

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.multipart.MultipartFile;


import com.example.restexample2.model.Board;

import com.example.restexample2.model.FileInfo;

import com.example.restexample2.util.HttpUtil;


@RestController

@RequestMapping ("/file")

public class FileController {

private static final Logger logger = LoggerFactory.getLogger(FileController.class);

     

     /**

      * 파일 멀티파트 업로드 Rest

      * {/

      * @param inputFile

      * @return 

      *  (주석 스타일 참고)

      */

     @RequestMapping(value = "/uploadFileModelAttribute/new", 

    method = {RequestMethod.POST },

    produces="text/plain;charset=UTF-8")

     public String multiFileUpload(@ModelAttribute Board boardVO, 

    @RequestParam("mediaFile")MultipartFile[] files, 

    Model model,

    HttpServletRequest req,

    HttpServletResponse res) throws IOException {

     

    boolean filechk = false;

     

         //디스크상의 프로젝트 실제 경로얻기

         //String contextRootPath = "c:" + File.separator + "upload";

    // String charset = "UTF-8";


    // req.setAttribute("charset", charset);

    // req.setCharacterEncoding(charset);

    // res.setContentType("text/html; charset=" + charset);

     

    String dirName = "upload" ; 

String contextRootPath = req.getSession().getServletContext().getRealPath("/") + dirName;

 

         System.out.println("실제경로:" + contextRootPath);


         //1. 메모리나 파일로 업로드 파일 보관하는 FileItem의 Factory 설정

         DiskFileItemFactory diskFactory = new DiskFileItemFactory(); //디스크 파일 아이템 공장

         diskFactory.setSizeThreshold(4096); //업로드시 사용할 임시 메모리

         diskFactory.setRepository(new File(contextRootPath + "/WEB-INF/temp")); //임시저장폴더

         

         //2. 업로드 요청을 처리하는 ServletFileUpload생성

         ServletFileUpload upload = new ServletFileUpload(diskFactory);

         upload.setSizeMax(3 * 1024 * 1024); //3MB : 전체 최대 업로드 파일 크기

         

         

         // 한글 깨짐 해결(버그)

         // String kor_a = new String(boardVO.getSubject().getBytes("8859_1"), "UTF-8");  

         System.out.println("게시물제목:" + HttpUtil.getISO8859toUTF8( boardVO.getSubject()) );

System.out.println("게시물작성자:" + HttpUtil.getISO8859toUTF8( boardVO.getName()) );

System.out.println("게시물내용:" + HttpUtil.getISO8859toUTF8( boardVO.getMemo()) );

System.out.println("파일(길이):" + files.length );

 

 

         for(MultipartFile mFile : files) {


             // 3. 파일 가져오기

    if ( mFile.getOriginalFilename().isEmpty() && 

      filechk == false ) {


            String msg = "Please select at least one mediaFile.<br/>(미디어 파일을 하나 이상 선택하십시오.)";

            model.addAttribute("msg", msg);

           

            return model.getAttribute("msg").toString();

    }

     

             // 4. 파일명 - 현재시간으로 생성

             String uploadedFileName = System.currentTimeMillis() + ""; 

             

        if (!mFile.getOriginalFilename().isEmpty()) {

         

        BufferedOutputStream outputStream = new BufferedOutputStream(

        new FileOutputStream(

        new File( contextRootPath + File.separator + "upload" + File.separator, uploadedFileName )

));

       

       

                  System.out.println("파일명:" + mFile.getOriginalFilename());

                 

                  outputStream.write(mFile.getBytes());

                  outputStream.flush();

                  outputStream.close();

                 

                  filechk = true;                 

              } 

         

         }

         

    return "fileUploadForm";

   

     }

     

}


파일명: FileController.java


[첨부(Attachments)]

FileController.zip


비고: HttpUtil.java의 HttpUtil에 정의된 한글 출력 문제 등이 적용되어 있음.




18. Controller - BoardRestController.java


경로: /src/main/java/com/example/restexample2/controller/BoardRestController.java


게시판 시스템을 적용한 Rest 컨트롤러이다.

자료구조를 적절하게 재배치하여 실습에는 DB없이 가능한 수준으로 구현하였다.


package com.example.restexample2.controller;


import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;


import javax.servlet.http.HttpServletRequest;


import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.web.bind.annotation.DeleteMapping;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.ModelAttribute;

import org.springframework.web.bind.annotation.PatchMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.PutMapping;

import org.springframework.web.bind.annotation.RequestBody;

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

import org.springframework.web.bind.annotation.RestController;


import com.example.restexample2.model.Board;


@RestController

@RequestMapping("/v1/api")

public class BoardRestController {

private static final Logger logger = LoggerFactory.getLogger(BoardRestController.class);


private static int num = 1;

private List<Object> tmpBoard = new ArrayList<Object>(); 

//@Autowired

    //private BoardService boardService;


    // 조회 = GET

    // (전체 게시물)

@GetMapping("board")

    public List<Object> listBoard(HttpServletRequest request, @ModelAttribute Board board) throws Exception {

   

    logger.info("게시판 목록");

    logger.info("----------------------------------");

   

    Board createNode = new Board();

    createNode.setId(num);

    createNode.setName("홍길동");

    createNode.setMemo("메모");

    createNode.setSubject("주소지");

   

    // tmpBoard.put(num, createNode);

    // num = num + 1;

   

    return tmpBoard;

    //return this.boardService.selectBoardList(request, board);

    }


    // 조회 = GET

    // (특정 세부 게시물)

    @GetMapping("board/{boardIdx}")

    public Board detailBoard(HttpServletRequest request, 

    @PathVariable(name="boardIdx", required=true) int boardIdx)

    throws Exception {


    logger.info("게시판 조회");

    logger.info("----------------------------------");

    logger.info("게시판 특정 게시물 번호" + boardIdx);

   

    //return this.boardService.selectBoard(request, boardIdx);

    return (Board) tmpBoard.get(boardIdx);

    }

    

    // 등록 = POST

    @PostMapping("board/new")

    public void insertBoard(HttpServletRequest request, @RequestBody Board board) throws Exception {

   

    logger.info("게시판 삽입");

   

    board.setId(num);


    tmpBoard.add(board);

    System.out.println(num);

    num = num + 1;

        //this.boardService.insertBoard(request, board);

    }


    // 수정 = PUT, PATCH (전송 방식)

    // /member/{id} + body (json데이터 등)

    @PutMapping("board/{boardIdx}")

    @PatchMapping("board/{boardidx}")

    public void updateBoard(HttpServletRequest request, 

    @PathVariable(name="boardIdx", required=true) int boardIdx, 

    @RequestBody Board board) throws Exception {

   

    logger.info("게시판 수정");

   

    if ( !tmpBoard.isEmpty() ) {

   

    board.setId(boardIdx); // 고유키 그대로 유지할 것

    tmpBoard.set(boardIdx - 1, board);

    }

    //board.setBoardIdx(boardIdx);

        //this.boardService.updateBoard(request, board);

   

    }

    

    // 삭제 = DELETE(전송 방식)

    @DeleteMapping("board/{boardIdx}")

    public void deleteBoard(HttpServletRequest request,

    @PathVariable(name="boardIdx", 

    required=true) int boardIdx) throws Exception {

   

    logger.info("게시판 삭제");

   

    try {   

    tmpBoard.remove(boardIdx);

    }

    catch(Exception e) {

        logger.info("Null값");

    e.getStackTrace();

    }

   

    //this.boardService.deleteBoard(request, boardIdx);

    }


}



파일명: BoardRestController.java


[첨부(Attachments)]

BoardRestController.zip




19. Util - HttpUtil.java


경로: /src/main/java/com/example/restexample2/util/HttpUtil.java


한글 언어에 대한 문제 해결에 대해서 정의하였다.


package com.example.restexample2.util;


import java.io.UnsupportedEncodingException;


public class HttpUtil {



    // 버그 개선: euc-kr 검증

    public static boolean isEucKr(String s) {

        int len = s.length();

        char c;

        for (int i = 0; i < len; i++) {

            c = s.charAt(i);

            /// System.out.println("" + c + " = " + toHex(c));

            if (((c & 0xFFFF) >= 0xAC00) && ((c & 0xFFFF) <= 0xD7A3))

                return true;

            /// else if (((c & 0xFF00) != 0) && ((c & 0x00) == 0))

            ///     return false;

        }

        return false;

    }

    // 버그 개선: ISO8859-1 검증

    public static boolean isISO8859(String s) {

     

        int len = s.length();

        char c;

        for (int i = 0; i < len; i++) {

            c = s.charAt(i);

            /// System.out.println("" + c + " = " + toHex(c));

            if ((c & 0xFF00) != 0)

                return false;

        }

        

        return true;

    }

    

    public static String getISO8859toUTF8(String s) {

   

    try {

return new String(s.getBytes("8859_1"), "UTF-8");

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

return s;

}

   

    }

}



파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil.zip



20. View - home.jsp


경로: /src/main/webapp/WEB-INF/views/home.jsp


기본 생성된 jsp파일이다.


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<%@ page session="false" %>

<html>

<head>

<title>Home</title>

</head>

<body>

<h1>

Hello world! - Rest(REST)

</h1>


<P>

</P>

</body>

</html>



파일명: home.jsp


[첨부(Attachments)]

home.zip



21. View - upload.jsp


경로: /src/main/webapp/WEB-INF/views/file/upload.jsp


업로드 페이지에 관한 정의이다.



그림 33. login.jsp 파일 모습 - 사용자 인터페이스(User Interfaces)


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<%@ page session="false" %>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>

<head>

<title>다중 파일 업로드</title>

<meta charset="UTF-8">

</head>

<body>

<h3>다중 파일 업로드 및 다중 변수</h3>


<form action="file/uploadFileModelAttribute/new" method="POST"

  enctype="multipart/form-data">

    <table>

        <tr>

            <td>

            제목:

            <input type="text" name="subject" >

            </td>

            <td>

            이름:

            <input type="text" name="name" >

            </td>

            <td>

            내용:

            <input type="text" name="memo" >

            </td>

        </tr>

        <tr>

            <td>Select Files(파일을 선택하시오)</td>

            <td>

            <input type="file" name="mediaFile" multiple>

            </td>

            <td>

            <input type="file" name="mediaFile" multiple>

            </td>

        </tr>

        <tr>

        <td colspan="3">

                <button type="submit">Upload(업로드)</button>

        </td>

        </tr>

    </table>

</form>

</body>

</html>



파일명: login.jsp


[첨부(Attachments)]

upload.zip



22. 의외로 어려운 REST 작업 - Client에서 작업하기


REST Client 앱을 설치하여 작업을 시도했을 때 명세 등을 정의하질 못해서 작업이 쉽게 이뤄지지 못한 경우도 있다.

그래서 하나 만들어보게 되었다.


비고: 작업할 때는 조금 많이 찍어봐야 한다. (힘들고 더딘 작업 중 하나이다.)



그림 34. REST Client에서 처리할 때 사용될 수 있는 정보들


이러한 정보는 그냥 나온 것은 아니고, 조금 코드로 셈플이 나오도록 찍어봐야 한다.

안 찍어보면, 어떤 구조인지 모른다. 알 길이 없다.



그림 35. 코드 등으로 객체를 찍어보는 형태로 변형해주기


셈플 코드가 출력될 수 있는 환경으로 코드를 변형해준다.



그림 36. 객체 정보의 체계 - 출력


이런 형태로 정보들이 나오면, 잘 대입해보고 정리해보기도 하고 그래야 한다.

(태스트 작업이 소요됨)



그림 37. YARC REST Client에서 사용하기


자료를 가공해서 입력한 후, Send Request를 누른다.



예를 들면, GET 관련 코드를 통해서 Send Request를 누른 후에 아래에서 결과를 찾아볼 수 있다.

그러면 아래처럼 관련 유추할 수 있는 정보 단위 코드를 출력해볼 수 있다.


"3":{"id":3,"subject":"주소지","name":"홍길동","memo":"메모"}}

(양식)


(응용 -> POST 등록 명령)

-> {"id":3,"subject":"주소지","name":"홍길동","memo":"메모"}



(응용 -> PUT, PATCH 수정 명령)

-> {"id":3,"subject":"주소지","name":"홍길동","memo":"메모"}



-----------------------------------------------------


POST /restexample2/file/uploadFileModelAttribute/new HTTP/1.1

Host: localhost:8080

Connection: keep-alive

Content-Length: 24211

Cache-Control: max-age=0

Upgrade-Insecure-Requests: 1

Origin: http://localhost:8080

Content-Type: multipart/form-data; boundary=----WebKitFormBoundarya1HOiexOytPpWx8U

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36 Edg/85.0.564.63

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

Sec-Fetch-Site: same-origin

Sec-Fetch-Mode: navigate

Sec-Fetch-User: ?1

Sec-Fetch-Dest: document

Referer: http://localhost:8080/restexample2/fileUploadView

Accept-Encoding: gzip, deflate, br

Accept-Language: ko,en;q=0.9,en-US;q=0.8,ja;q=0.7,de;q=0.6

Cookie: JSESSIONID=C1BB03C6629263A6B77084AA15CDF947

         }


파일명: sample-keyword.txt


[첨부(Attachments)]

sample-keyword.txt

sample-keyword.zip




* 맺음글(Conclusion)


실전에서 REST를 쉽고 빠르게 적용할 수 있는 방법이 없는지 충분히 고민하였다.

시중 책도 정말 많이 사보고, 인터넷 정보 검색, 공식 메뉴얼 등 삽질을 많이 하였다.


그리하여 이 프로젝트가 작성된 것이다.

REST를 쉽고 빠르게 사용할 수 있었으면 하는 바람이다.



* 참고자료(References)


[RESTEasy - 프로젝트에 대한 것]

1. A Guide to RESTEasy, https://www.baeldung.com/resteasy-tutorial, Accessed by 2020-09-28, Last Modified 2020-02-12.

2. tutorials/resteasy at master · eugenp/tutorials · GitHub, https://github.com/eugenp/tutorials/tree/master/resteasy, Accessed by 2020-09-28, Last Modified .

3. jersey, resteasy(JBoss)

[비고] 

-> resteasy는 현재 xml 출력을 못함. (POST, GET 등은 동작함)

-> jersey는 최신 버전도 인식을 못해버림.


[RestController, RESTful Service - Spring Framework ]

4. Building a RESTful Web Service, https://spring.io/guides/gs/rest-service/, Accessed by 2020-09-28, Last Modified .

[참고할 때 메모]

-> Library없이 가능한지 확인할 것 

[비고]

-> Gradle 기반과 Spring Boot로 작성되었는데, Gradle 생략하고 Spring Framework 작업은 동일해서 많은 도움을 받았다.


5. RestController에 대해 알아보자, https://milkye.tistory.com/283, Accessed by 2020-09-28, Last Modified 2018-12-02.

[비고]

매우 간단하게 RestController를 사용하는 방법에 대해서 소개하고 있음.


6. [Spring Error] No converter found for return value of type: class java.util.ArrayList, https://keichee.tistory.com/274, Accessed by 2020-09-28, Last Modified 2016-11-27.

[비고]: jackson-bind의 적용 방법에 대해서 소개하고 있음. 클래스 변환에 대한 오류 해결은 미약함.


[다중 업로드]

7. Spring MVC - 유연한 다중 파일업로드 핸들러 만들기 (Multipart upload), https://galid1.tistory.com/684, Accessed by 2020-09-28, Last Modified 2020-01-29.


8. 스프링 파일 업로드 처리, https://advenoh.tistory.com/26, Accessed by 2020-09-28, Last Modified 2019-01-01.



[RESTful - 게시판 설계]

9. 4. springboot restful 방식으로 게시판 변경, https://linked2ev.github.io/gitlog/2019/12/28/springboot-restful-4-rest-%EB%B0%A9%EC%8B%9D%EC%9C%BC%EB%A1%9C-%EB%B3%80%EA%B2%BD/ , Accessed by 2020-09-28, Last Modified 2019-12-28.

[비고]: REST 기반의 게시판 설계 아이디어를 많이 얻었음.


[RESTTemplate 관련]

10. Spring REST API 생성, 호출, https://www.leafcats.com/173, Accessed by 2020-09-28, Last Modified 2017.


11. 스프링 (Spring) RestTemplate으로 POST 파라미터 (Parameter) 전송해보기, https://soshanstory.tistory.com/entry/스프링-Spring-RestTemplate으로-POST-파라미터-Parameter-전송해보기, Accessed by 2020-09-28, Last Modified 2014-08-31.

[비고]: Rest Template 사용방법을 매우 간단하게 잘 적어놨음.


[공부하면서 메모]

- XML, Java 설정 방식이 있다. (REST 셋팅 관련 - 범위가 방대해지므로 줄임)


12. RestTemplate (4가지 - 자바 내에서 클라이언트 작업), https://howtodoinjava.com/spring-boot2/resttemplate/spring-restful-client-resttemplate-example/, Accessed by 2020-09-28, Last Modified 2015-03.

[비고]: 추천하고 싶음. 매우 RestTemplate에 대해서 가장 깔끔하게 잘 정리하였음.


(공식적으로는 HTML에서는 POST, GET만 지원함.)


13. RestTemplate list 반환하기, https://luvstudy.tistory.com/52, Accessed by 2020-09-28, Last Modified 2018-11-16.


[REST 한글 깨짐 Response]

14. spring에서 json response 한글 깨짐, https://thswave.github.io/spring/2015/02/22/korean-json-response.html, Accessed by 2020-09-28, Last Modified 2015-02-22.


[POST, GET 이외의 문제]

15. Using PUT method in HTML form, https://stackoverflow.com/questions/8054165/using-put-method-in-html-form, Accessed by 2020-09-28, Last Modified 2012.

[비고]: 8년 전 문제이긴 하지만, 현재에도 해당되는 문제이다.


16. REST - HTML Form에서 GET/POST만 지원하는 이유, http://haah.kr/2017/05/23/rest-http-method-in-html-form/, Accessed by 2020-09-28, Last Modified 2017-05-23.

[비고]: 이론적으로 REST에 대해서 아주 잘 소개하고 있는 사이트이다.


17. HTML 양식에 PUT 및 DELETE 메소드가없는 이유는 무엇입니까?, https://qastack.kr/software/114156/why-are-there-are-no-put-and-delete-methods-on-html-forms, Accessed by 2020-09-28, Last Modified .

[비고]: 토론 형태로 문제에 대해서 의견을 공유하고 있다.


18. [REST] PUT, PATCH, DELETE 미지원 처리, https://velog.io/@ette9844/REST-PUT-PATCH-DELETE-%EB%AF%B8%EC%A7%80%EC%9B%90-%EC%B2%98%EB%A6%AC, Accessed by 2020-09-28, Last Modified 2020-05-19.

[비고]: 이 코드는 동작하지 않음. (이론적으로 동작되는 방법이라고 보는 게 타당함.)


[번외의 주제: Node.js - REST]

19. REST API 예제, https://hyun-am-coding.tistory.com/entry/REST-API-%EC%98%88%EC%A0%9C, Accessed by 2020-09-28, Last Modified 2019-11-17.

[비고]: REST Client와 Server 흐름에 대해서 살펴볼 수 있다. 물론 자바 코드에 직접 도움은 되는 건 아니지만 동일하게 구성될 수 있다는 아이디어를 제공해준다.



반응형
728x90
300x250

[Spring-Framework] 19. Spring MVC - Spring Framework 5 REST, Jackson, Commons-FileUpload - (1)


프로젝트 위주로 불필요한 라이브러리는 전부 다 제거하고 순정으로 REST를 이해할 수 있도록 프로젝트를 기획하게 되었다.

REST를 제대로 소개하는 분들이 조금 적어서 간단하면서도 알기 쉽게 소개하려고 한다.

Spring Boot 위주의 REST가 많이 있는데, Spring Framework 5로 구현해도 무방하다.

결론부터 이야기하자면, 매우 잘 된다고 할 수 있다.


REST 이론부터 프로젝트까지 실질적으로 소개하려고 한다.


[사전 배경 지식]

- HTML, 간단한 폼 전송 방식 이해
- 자료구조(Data Structures)
  (DB 이런 거 전부 제거함.)


[기타]

복잡하게 SOA 등 어렵고 관련 없는 주제는 다 제거하였다.

실질적으로 사용될 수 있는 수준의 REST에 대해서 소개해보려고 한다.



작업 환경

- IDE: Spring-tool-suite 4-4.7.2. Releases. / 최신(2020-09-29)

- Maven 3.6.3/1.16.0.20200610-1735 / 최신(2020-09-29)

- Java Version: 14이상 (OpenJDK 15) / 최신(2020-09-29)

pom - maven

- Spring Framework 5.2.9. RELEASES. / 최신(2020-09-29)

- javax.servlet-api 4.0.1 (2018-04-20) / 최신(2020-09-29)

   https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api/4.0.1

- jackson-core 2.11.2  - XML (2020-08-02) / 최신(2020-09-29)

- jackson-databind 2.11.2 - XML (2020-08-02) / 최신(2020-09-29)

- commons-fileupload 1.4  (Apache Commons, 2018-12-24) / 최신(2020-09-29)

- WAS: Apache-tomcat-9.0.37-windows-x64 / 최신(2020-09-29)



1. 실험 결과


(1차)

- Maven Project (webapp)

  Resteasy(JBoss 기반)

  -> 웹 브라우저로 출력을 시도했을 때, XML 변환이 안 됨. (GET, POST, PUT, PATCH, DELETE)는 동작함.

     (자료가 많이 부족함.)


  [첨부(Attachments)] (영어)

resteasy-reference-guide-en-US.pdf


결론: 동작은 되나 XML 변환 도구를 찾을 수 없음. (실패)


(2차)

- Maven Project (webapp)

  Jersay (4~5개 이상 POM 요구됨.)

  

과거에는 호환성이 나름대로 있었다고 하나, 현재에는 버전이 전부 제각각이고 사용하기에 부적합하다고 주장함.

(2020-09-28)

참고: 과거에는 com.sun..... jersey 형태의 패키지에서 현재는 "Jersey"가 독립 프로젝트로 분리되었음.

   javax가 일부 명칭이 변경되었음.


  [첨부(Attachments)] (영어)

jersey-documentation-3.0.0-M1-user-guide.pdf


결론: 동작 자체가 안 됨. (실패)


(3차)

- Spring Framework 5.4

  - Spring Legacy Project -> Spring MVC Project

    -> Pom(Maven) : 

        Spring Framework 5.4 - 2020-09-28일 기준 최신

        Servlet 4.0.1 - 2020-09-28일 기준 최신

        Jackson(core, bind) = 용도: XML Parser 2.11.2 - 2020-09-28일 기준 최신

        Apache Commons-FileUpload


결론: 성공





2. REST - 구현 관점에서 이론 소개


복잡하게 다른 걸 알 필요는 없다고 보고, HTTP 프로그램 작성해봤으면 POST/GET 이걸 다뤄본 경험들이 있을 것이다.

해외에 있는 "(Ph.D / 공학/이학 계통의 박사) 로이 필딩이 발표하신 학위 논문이 전 세계에 널리 알려진 거라고 보는 게 좋겠다.


https://roy.gbiv.com/


관심이 있는 분들은 로이 필딩 박사 홈페이지에서 대략적인 이력을 살펴봐도 무방하다.

로이 필딩 박사의 주요 업적은 "Apache Web Server", "REST(학위 논문)" 두 가지라고 보면 된다.


웹 분야에서는 매우 권위있는 분 중 하나라고 생각하면 된다.


전송 방식의 문제인데 "POST/GET"만 웹에서는 가능하다. (HTML5 현재까지도)


REST의 약자인 REST(Representational State Transfer)는 GET/POST의 한계를 현실세계의 상태 개념과 접목 시켜서 고민을 한 모양이다.

(필자는 비록 논문을 읽어보진 못했지만, 작업을 하면서 느껴본 견해이다.)


핵심을 요약하면,


표 1. 다양한 전송방식(REST)


번호 

작업

전송 방식

1

Create

POST

2

Read

GET

3

Update

PUT 또는 Patch

4

Delete

DELETE


REST는 4가지 전송 방식으로 확장되었다.


* 자바스크립트의 (Ajax)로 REST를 전송할 수 있는가?

-> 구현되었다고 주장하는 시중 코드들이 있을 수는 있겠으나 안 될 수도 있다.
   (= 국제 웹 관련 표준기구(W3C) REST를 GET, POST 이외에도 표준으로 인정해주지 않는 이상 어렵다고 본다.)


표 2. 회원(Member) 자원에 대한 전송 방식 정의


작업

전송방식 

URI 

생성

POST

/member/new 

조회(전체)

GET

/member

조회(특정)

GET

/member/{id}

수정

PUT or PATCH

/member/{id}

삭제

DELETE

/member/{id}


꼭 이런 정의가 반드시 표준이라는 건 아니다. 일부 책들을 많이 대조해보고, 인터넷 검색 등을 해봤으나 "이런 형태로 쓸 수 있다." 이렇게 생각하면 좋겠다.

도메인이 달라지면, 달라지는 데로 바꿔서 사용해야 한다.




3. REST - 웹 표준 관점에서 무엇이 문제가 되는가?


코드 구현으로 이러한 기술적 문제를 소개해주려고 한다.


<form method="PUT">

~~~(중략)

</form>


코드 1) 이론적으로는 이렇게 되어야 함. (미지원)


<form method="POST">

<input type="hidden" name="_method" value="PUT"> 

~~~(중략)

</form>


코드 2) 과거 잠시 표준 규격에서 예외를 해주었던 규격 (미지원)


코드 2 형태로 구현하면, 동작될 수도 있었던 잠시 과거가 있긴 있었다.

표준 기구 등에서 제한에 걸어서 동작하지 않은 코드들이다.


(슈도 코드)
<script>

    .....

   


    ajax{

           contents/json

           method = "PUT" action=......


           sucessed{

           }

           failed{



           }

    }

</script>
~~~(중략)

<form method="POST">

<input type="hidden" name="_method" value="PUT"> 

~~~(중략)

</form>


코드 3) ajax 자바스크립트 - 슈도 코드 (미지원)


이러한 코드 형태도 태스트해본 결과로는 동작하지 않는다.



그림 1. RequestMethod.PATCH, RequstMethod.PUT (Spring-Framework 5.4)


그림 1의 코드 형태로 정의했을 때, 전송이 되냐는 것이다.

결론부터 이야기하면, 안 된다.


다 안 된다고 적었으니, 그러면 "REST는 전혀 사용할 수 없는 건가요?"라는 물음이 들 수 있다.

이야기하면, 사용할 수 있다.


그런데 사용할 수 있는 용도가 있다는 것이다.


예1) 안드로이드 - 게시판 개발, 

예2) 각종 규격 문서 또는 메시지 전달


(이런 부분에서 사용하면 적합하다고 본다.)


-> 원격지에서의 파일 전송과 내려받기는 괜찮을까요?

   결론부터 놓고 보면, 안 된다. (원격지 REST를 활용해서 전송할 수 있는가?)

   이론적으로는 Multipart로 해서 하면 될 것 같은데 POST에서는 처리될 수도 있겠으나, 어려울 수 있다.

   = 표 2의 명세에서는 "등록(POST 방식)", "수정(PUT)" 방식을 소개하고 있는데, 다소 어렵다고 하는 이야기가 이런 부분이다.


굳이 어렵게 "전송 해더(Header)" 찾고 등의 고도의 작업을 시도해도 안 되는 이유가 보안이라는 주제 때문에 웹 브라우저 등에서 제약이 되어 버린다.



[호기심 자극하기]

안드로이드 어플을 사용하다 보면, 업로드 기능이 있는 경우에는 순수한 안드로이드 기술로 개발한 게 아니라 다른 웹 사이트를 호출해서 사용한 것이다.

개발 관점에서는 안 된다가 타당하다고 본다.


JSP, PHP, ASPX, ASP 등의 개발이 되니깐 그걸 그냥 그대로 활용한 것이다.




4. URI, URL에 대해서


* URI(Uniform Resource Identifier) : 자원의 식별자라는 의미
* URL(Uniform Resource Location) : 주소


URL은 URI의 하위개념이므로 혼용해도 무방할 수 있다.

이론적으로도 비슷하게 취급될 여지도 있어서 크게 용어를 따로 이중으로 외워둘 필요까진 없겠다.




5. REST의 실질적인 기능 (서버, 클라이언트 관계)


REST는 XML-RPC처럼 통신 계열로 보는 게 적합하다고 본다.

태스트를 하다보면, 육감적으로 왜 그렇게 보는 게 타당한지 이해할 수 있을 것으로 보인다.



그림 2. REST 전용 태스트 도구 - 브라우저 내 추가한 앱스(YARC! Yet Another REST Client)


가장 사용하기에 좋은 REST 태스트 도구라고 본다.

이 방식을 흔히 많이 소개하고 있는데, 코드로도 서버를 하나 구축해서 사용할 수 있다.




그림 3. RestTemplate를 이용한 Server 만들기 - 자바(스프링 프레임워크 5.4)



그림 4. Spring MVC Controller를 이용한 Client 만들기(RestController 등) - 자바(스프링 프레임워크 5.4)


정리를 해보면, 아래의 도식처럼 요약해볼 수 있다.




그림 5. REST Client와 Server 관계


이런 느낌으로 사용하게 된다.

이런 관계인 이유는 메시지 형태로 데이터를 반환하면, "객체, 메시지" 등의 정보를 송/수신 할 수가 있다.


어노테이션 

기능 

@RestController

Controller가 REST 방식을 처리하기 위해 명시한 것 

@ResponseBody

일반적인 JSP와 같은 뷰로 전달되는 게 아니라 데이터 자체를 전달하기 위한 용도 

@PathVariable

URL 경로에 있는 값을 파라미터로 추출하려고 할 떄 사용 

@CrossOrigin

Ajax의 크로스 도메인 문제를 해결해주는 어노테이션  

@RequestBody

JSON 데이터를 원하는 타입으로 바인딩 처리 


실질적으로 @RestController는 필수 정의이다.

@PathVariable과 함께 조합해서 가장 많이 사용할 것으로 보인다.


운이 나쁘면, 크로스 오리진 등은 접할 수도 있고, 안 접할 수도 있다.



6. 프로젝트 - 소개


복잡한 주제는 조금 정리하고 실질적으로 꼭 필요한 형태로 이러한 기능들을 모두 담고 있는 종합적인 REST만 전문으로 하는 프로젝트를 작성하였다.



그림 6. 프로젝트 구성도


다른 프로젝트 등에 비해서 매우 깔끔하고 간단한 형태로 REST를 취급할 수 있도록 신경을 조금 써서 작성한 것이다.

데이터베이스 지식, MyBatis(iBatis) 등 그런 거 일절 신경 안 쓰고 다뤄볼 수 있으니 참고하면 되겠다.



그림 7. 결과 1(client/listMapDelete/{id})



그림 8. 결과 2(/v1/api/board)의 JSON



그림 9. 결과 3(/v1/api/board)의 JSON (결과 출력)




그림 10. 결과 4(/v1/api/board/new)의 JSON(POST 전송)



그림 11. REST 전송 방식들


굉장히 많은 전송 방식이 있다는 것을 알 수 있다.



그림 12. 다중 업로드 프로젝트 (1) - REST 활용



그림 13. 다중 업로드 프로젝트 (2) - REST 활용



그림 14. 다중 업로드 프로젝트 (3) - 업로드 반응



그림 15. 다중 업로드 프로젝트 (4) - 업로드 반응



그림 15. 다중 업로드 프로젝트 (4) - 실제 업로드 모습





7. 프로젝트 생성


프로젝트 생성부터 단계적으로 아주 친절하게 하도록 하겠다.


[선수 준비되어야 할 부분]

- 필자의 글을 보면, 이 부분이 아주 자세히 소개되고 있는데 잊어버리기 쉬워서 다시 계속 강조하는 것이다.

한 번 셋팅해놓으면 크게 무방한데, 안 될 경우 등 문제를 충분히 고민한 후에 포함시킨 것이니깐 이해 해주었으면 좋겠다.


Help-> Eclipse Marketplace -> STS 검색 -> Spring-Tool Add-on



그림 16. Help의 Eclipse Marketplace



그림 17. Help의 Eclipse Marketplace


Spring Tools 3 Add-On for Spring Tools 4 3.9.14 Release를 선택한 후 Install을 해준다.

-> Spring Framework 3.2인가 "Spring Legacy Project" 기능을 최신 버전에서도 사용할 수 있도록 도와주는 도구이다.


Spring Legacy Project만 동작되어도 기본 셋팅하는 데 있어서 매우 수월해진다.




그림 18. Eclipse의 파일 메뉴 모습


File->New->Spring Legacy Project를 클릭한다.



그림 19. Spring MVC Project 생성하기 - Spring Legacy Project 기능


Spring MVC Project를 선택한다.

Project Name을 입력한다.

Next를 누른다.



그림 20. 패키지 정보 입력 - Spring MVC Project 생성하기


패키지명을 입력한다.

Finish를 누른다.




8. 자바 버전 바꾸기 - 프로젝트 Properties의 Build Path, Project-Factes


자바 버전을 바꿔줘야 한다.

이유는 Spring Legacy Project의 기본 셋팅이 하위버전으로 되어있기 때문이다.



그림 21. 프로젝트의 Properties 클릭하기 (프로젝트 마우스 오른쪽 버튼의 메뉴 모습)


프로젝트를 선택한다.

마우스 오른쪽 버튼을 클릭한다.

"Properties"를 클릭한다.



그림 22. Properties - Build Path


JRE System Library를 선택한다.

Edit를 누른다.

JRE를 클릭한다.

14버전으로 바꿔준다.

Apply를 누른다.



그림 23. Properties - Project Factes


Java의 버전을 14버전으로 바꿔준다.

Apply를 누른다.

Apply and Close를 누른다.



9. pom.xml 환경설정하기


꼭 열어봐야 할 사이트가 있다.

http://mvnrespository.com 사이트에 접속해서 버전 등을 확인해줘야 한다.


검색 방법을 잊어버린 사람들을 위해서 특별히 다시 언급하겠다.



그림 24. Spring Framework 검색 결과 - mvnrepository


Spring Framework를 클릭한다.



그림 25. Spring Framework 검색 결과 - mvnrepository


5.2.9.RELEASE를 선택한다.




그림 26. Spring Framework 검색 결과 - mvnrepository


Maven의 POM 배포 코드를 복사한다.



그림 27. pom.xml 수정작업(1) - 자바 버전과 스프링 프레임워크 버전 업그레이드


자바 버전과 Spring-Framework 버전을 변경해준다.

참고로 Spring-Framework의 경우에는 일명 "깔맞춤"으로 자동 버전 셋팅이 되는 경우가 있다.


다 되는 건 아니니깐 찾아보는 작업을 잊지 않아야 한다.



그림 28. pom.xml 수정작업(2)


javax.servlet 버전도 업그레이드 해준다.




그림 29. pom.xml 수정작업(3)


jackon-core, jackson-databind, commons-fileupload를 각각 검색해서 "복사, 붙여넣기"를 해준다.


<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>

<artifactId>restexample2</artifactId>

<name>restExample2</name>

<packaging>war</packaging>

<version>1.0.0-BUILD-SNAPSHOT</version>

<properties>

<!-- 자바 버전(14로 작성) -->

<java-version>14</java-version>

<!-- 스프링 프레임워크 - 최신 버전 적용함 -->

<org.springframework-version>5.2.9.RELEASE</org.springframework-version>

<org.aspectj-version>1.6.10</org.aspectj-version>

<org.slf4j-version>1.6.6</org.slf4j-version>

</properties>

<dependencies>

<!-- Spring -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>${org.springframework-version}</version>

<exclusions>

<!-- Exclude Commons Logging in favor of SLF4j -->

<exclusion>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${org.springframework-version}</version>

</dependency>

<!-- AspectJ -->

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjrt</artifactId>

<version>${org.aspectj-version}</version>

</dependency>

<!-- Logging -->

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>${org.slf4j-version}</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>jcl-over-slf4j</artifactId>

<version>${org.slf4j-version}</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>${org.slf4j-version}</version>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>1.2.15</version>

<exclusions>

<exclusion>

<groupId>javax.mail</groupId>

<artifactId>mail</artifactId>

</exclusion>

<exclusion>

<groupId>javax.jms</groupId>

<artifactId>jms</artifactId>

</exclusion>

<exclusion>

<groupId>com.sun.jdmk</groupId>

<artifactId>jmxtools</artifactId>

</exclusion>

<exclusion>

<groupId>com.sun.jmx</groupId>

<artifactId>jmxri</artifactId>

</exclusion>

</exclusions>

<scope>runtime</scope>

</dependency>


<!-- @Inject -->

<dependency>

<groupId>javax.inject</groupId>

<artifactId>javax.inject</artifactId>

<version>1</version>

</dependency>

<!-- Servlet -->

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>

<dependency>

<groupId>javax.servlet.jsp</groupId>

<artifactId>jsp-api</artifactId>

<version>2.1</version>

<scope>provided</scope>

</dependency>

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>jstl</artifactId>

<version>1.2</version>

</dependency>

<!-- Test -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.7</version>

<scope>test</scope>

</dependency>       

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->

<dependency>

    <groupId>com.fasterxml.jackson.core</groupId>

    <artifactId>jackson-core</artifactId>

    <version>2.11.2</version>

</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->

<dependency>

    <groupId>com.fasterxml.jackson.core</groupId>

    <artifactId>jackson-databind</artifactId>

    <version>2.11.2</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->

<dependency>

    <groupId>commons-fileupload</groupId>

    <artifactId>commons-fileupload</artifactId>

    <version>1.4</version>

</dependency>

 

</dependencies>

    <build>

        <plugins>

            <plugin>

                <artifactId>maven-eclipse-plugin</artifactId>

                <version>2.9</version>

                <configuration>

                    <additionalProjectnatures>

                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>

                    </additionalProjectnatures>

                    <additionalBuildcommands>

                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>

                    </additionalBuildcommands>

                    <downloadSources>true</downloadSources>

                    <downloadJavadocs>true</downloadJavadocs>

                </configuration>

            </plugin>

            <plugin>

                <groupId>org.apache.maven.plugins</groupId>

                <artifactId>maven-compiler-plugin</artifactId>

                <version>2.5.1</version>

                <configuration>

                    <source>1.6</source>

                    <target>1.6</target>

                    <compilerArgument>-Xlint:all</compilerArgument>

                    <showWarnings>true</showWarnings>

                    <showDeprecation>true</showDeprecation>

                </configuration>

            </plugin>

            <plugin>

                <groupId>org.codehaus.mojo</groupId>

                <artifactId>exec-maven-plugin</artifactId>

                <version>1.2.1</version>

                <configuration>

                    <mainClass>org.test.int1.Main</mainClass>

                </configuration>

            </plugin>

        </plugins>

    </build>

</project>



파일명: pom.xml


[첨부(Attachments)]

pom.zip




10. web.xml - 환경설정하기


web.xml 파일 셋팅은 무척 중요하다고 볼 수 있다.

반드시 해야 하는 작업이다.


경로명: /src/main/webapp/web.xml




그림 30. web.xml


<?xml version="1.0" encoding="UTF-8"?>

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

  <context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>/WEB-INF/spring/root-context.xml</param-value>

  </context-param>

  <listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

  </listener>

  <servlet>

    <servlet-name>appServlet</servlet-name>

    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <init-param>

      <param-name>contextConfigLocation</param-name>

      <param-value>

      /WEB-INF/spring/appServlet/servlet-context.xml

      /WEB-INF/spring/spring-file-config.xml

      </param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

  </servlet>

  <servlet-mapping>

    <servlet-name>appServlet</servlet-name>

    <url-pattern>/</url-pattern>

  </servlet-mapping>

  <servlet>

    <description></description>

    <display-name>RestController</display-name>

    <servlet-name>RestController</servlet-name>

    <servlet-class>com.example.restexample2.controller.RestController</servlet-class>

  </servlet>

  <servlet-mapping>

    <servlet-name>RestController</servlet-name>

    <url-pattern>/RestController</url-pattern>

  </servlet-mapping>

</web-app>


파일명: web.xml


[첨부(Attachments)]

web.zip



11. spring-file-config.xml - 파일 생성하기


생성경로: /src/main/webapp/WEB-INF/spring/spring-file-config.xml


왜 작업을 해주냐면, Apache Commons-Fileupload를 사용하기 위해서이다.



그림 31. spring 폴더의 오른쪽 버튼 메뉴 모습


spring 폴더를 마우스 오른쪽 버튼으로 클릭한다.

New-> File을 클릭한다.


"spring-file-config.xml" 파일을 만들어준다.




그림 32. spring-file-config.xml - 작업 모습


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- Root Context: defines shared resources visible to all other web components -->

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

    <property name="maxUploadSize" value="10485760"/>

    <property name="maxUploadSizePerFile" value="10485760"/>

    <property name="maxInMemorySize" value="0"/>

</bean>

</beans>



파일명: spring-file-config.xml


[첨부(Attachments)]

spring-file-config.zip




* 2부에서 만나요.


2부에서는 Controller와 Model, View, Util의 주제를 다뤄보겠다.


1. [Spring-Framework] 19. Spring MVC - Spring Framework 5 REST, Jackson, Commons-FileUpload - (1), Accessed by 2020-09-28

https://yyman.tistory.com/1426


반응형

+ Recent posts