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
[JSP] 5. Apache Tomcat 9 윈도우 10에서 수동으로 설치하기(Manually installing in Apache Tomcat 9 window)

 

MS윈도우 환경에서 아파치 톰캣 9를 설치하는 방법에 대해서 소개합니다.

(Here's how to install Apache Tomcat 9 in an MS Windows environment.)

 


1. 아파치 톰캣 공식 사이트에 접속하기(Access the Apache Tomcat official site)

 

https://tomcat.apache.org/download-90.cgi에서 다운 받을 수 있습니다.

"You can download it from `https://tomcat.apache.org/download-90.cgi.`"

 

그림 1) Apache 톰캣 공식 웹 사이트(Apache Tomcat official website)

 


2. 다운받은 파일 압축 풀기(Extract the downloaded file)

 

다운받은 파일을 압축 풉니다.(Unzip the downloaded file.)

 

 

그림 2) 다운받은 파일 압축 풀기

Figure 2) Extract the downloaded file

 

 

Figure 3) Extract the downloaded file

 

 

Figure 4) Extract the downloaded file

 


3. 압축 푼 폴더 C:\로 이동하기(Go to the extracted folder C:\)

 

압축 푼 폴더를 "잘라내기", "붙여넣기"로 이동시켜줍니다.

(Move the extracted folder to "Cut" or "Paste".)

 

 

그림 5) 복사 붙여넣기가 완료된 아파치 톰캣 폴더

Figure 5) Apache Tomcat folder with copy paste complete

 


4. 서버 실행하기(Run the server)

 

"c:\apache-tomcat.xxx\bin"에서 "startup.bat"을 더블클릭합니다.

(Double click "startup.bat" in "c:\ apache-tomcat.xxx\bin".)

 

 

그림 6) 서버 실행파일이 존재하는 위치

Figure 6) Location of Server Executables

 

 

그림 7) OpenJDK 또는 JDK 액세스 허용을 클릭하기 전의 모습

Figure 7) Before clicking OpenJDK or Allow JDK access

 


5. 아파치 톰캣 사이트(localhost) [Apache Tomcat site (localhost)]

 

http://127.0.0.1:8080 또는 http://localhost:8080에 접속하면 아래의 화면을 볼 수 있습니다.
(If you access http://127.0.0.1:8080 or http://localhost:8080, you will see the following screen.)

 

그림 8) 아파치 톰캣 처음화면

Figure 8) Apache Tomcat Home

 

 

그림 9) 아파치 톰캣에 사용되는 JSP, Servlet 예제 사이트

Figure 9) JSP, Servlet example site used by Apache Tomcat

 

반응형
728x90
300x250

[PHP] OTP와 로그 시스템 (OTP and Log System)

 

root at 127.0.0.1

 

이번에 소개할 것은 OTP와 IP로그 시스템에 관해서 심도적으로 소개하려고 한다.

사용자 인터페이스 관점에서의 OTP는 크게 하드웨어와 소프트웨어로 분류할 수 있다고 가설을 두고 설계하였다.

보안 이슈에서 OTP 시스템이 도입된 지 불과 몇 년 되지 않았다고 본다.

IP 로그와 OTP 시스템의 차이점을 소개하겠다.

 

 


1. OTP란?

 

One time Password라는 용어로 일회성 비밀번호를 생성하는 시스템이라고 부른다.

예를 들면, 사용자가 보유하고 있는 비밀번호 체계로 인증체계를 구현할 때 사용되어진다.

 


2. 사용자 인터페이스 설계하기(Designing the User Interface)

 

사용자 관점에서 "OTP" 인터페이스를 고안하였다.

크게 어렵지 않은 인터페이스 화면으로 디자인 할 수 있다.

 

 

그림 2-1. 소프트웨어 - OTP Generator(OTP 생성기) - 도도(Dodo)

그림 2-1은 소프트웨어 형태의 OTP 생성기이다.

 

 

그림 2-2. 하드웨어 OTP Generator(Hardware OTP Generator) - 도도(Dodo)

 

그림 2-2는 하드웨어 형태의 OTP 생성기이다.

 


3. 웹 페이지에서의 OTP 생성하기(Creating an OTP on a Web Page)

 

아래의 그림은 웹 페이지에서 OTP를 생성하는 것이다.

 

그림 3-1. OTP 생성 구현의 예 - 도도(Dodo)

 

OTP 생성에 관한 소스코드이다. 크게 어렵지 않은 구조로 작성할 수 있다.

 

<?php
/*
 *  Created Date : 2018-08-29
 *  Filename: index.php
 *  Author: Dodo
 */
?>

<?php

function generate(){
    return time() * mt_rand(1, 5);   
}

?>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta name="viewport" content="width=320; user-scalable=no" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
 <title>OTP 발생기(OTP Generator)</title>
 <link rel="stylesheet" type="text/css" href="./css/mystyle.css">
</head>
<body>

<!-- OTP Generator Model -->
<table style="width: 100%;">
 <tr>
  <td style="background-color: #FA5882; height:40px">
   <span style="font-size:20px; color:#FFF; font-weight:bold;">
    OTP Generator(OTP 생성기)
   </span>
  </td>
 </tr>
 <tr>
  <td>
   <input type="text" class="otp" name="otp" value="<?php echo generate(); ?>" style="width:100%;height:30px;">
  </td>
 </tr>
 <tr>
  <td>
   <input type="submit" class="generate" value="생성하기" onclick="window.location.reload();">
  </td>
 </tr>
</table>

</body>
</html>

 

 

 

영상 1. OTP 생성기 / 시연 - 도도(Dodo)

 


4. 웹 페이지에서 로그

 

웹 페이지에서 사용자 로그를 생성하는 것에 대해서 소개한다.

 

CREATE TABLE IF NOT EXISTS `log` (
      `id` int(11) NOT NULL auto_increment,
      `createDate` datetime NOT NULL,
      `subject` text NOT NULL,
      `ip` text NOT NULL,
      PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

 

데이터베이스의 테이블 설계는 다음과 같이 간단한 구조로 설계할 수 있다.

 

 

위의 코드를 실행하면 아래처럼 반응하는 것을 볼 수 있다.

 

 

그림 4-1. 웹 페이지에서의 로그 - 도도(Dodo)

 

 

영상 2. 로그 생성 반응 시연 - 도도(Dodo)

 


5. 아파치 웹 서버

 

아파치 웹 서버는 아파치 파운데이션(Apache Foundation)에서 만든 프로젝트이다.

http://www.apache.org

 

 

그림 5-1. 아파치 소프트웨어 재단(Apache Software Foundation) - 도도(Dodo)

 

                   표 1. 영어, 한글로 읽어보기

 

번호

영어 단어

한글 읽기(Hangul)

1

Open

개방, 열다

오픈

2

Software

소프트웨어

소프트웨어

3

Soft

부드러운

소프트

4

Innovation

혁신

이노베이션

5

Community

커뮤니티(공동체)

커뮤니티

6

Consider

고려하다,
중히 여기다

컨시더

 

 

그림 5-2. 아파치 프로젝트 소개 - 도도(Dodo)

 

                                                                         표 2. 영어, 한글로 읽어보기

 

번호

영어 단어 또는 문장

한글 읽기

1

What is the Apache HTTP Server Project?

아파치 HTTP 서버 프로젝트는 무엇입니까? 

 왓 이스 더 아파치 에이치티티피

서버 프로젝트?

2

The Apache HTTP Server Project is a collaborative software development

Apache HTTP Server 프로젝트는
공동 소프트웨어 개발입니다.

디 아파치 에이치티티피 서버 프로젝트 이스 어 콜라브레이티브 소프트웨어 디벨러프멘트.

3

Collaborative

공동

콜라보레이티브

4

effort

노력

에포트

5

Mailing Lists

메일링 리스트

메일링 리스트

6

Trunk (dev)

 트렁크 (개발)

뜻(의역): 저장 보관소

트렁크 (디브)

 

 


6. 맺음글(Conclusion)

 

OTP 시스템과 로그 시스템에 대해서 살펴보았다.

 


7. 참고자료(Reference)

 

1. OTP, Last Modified , Accessed by 2018-08-29, https://ko.wikipedia.org/wiki/일회용 비밀번호

2. PHP: mysql_query, Last Modified, Accessed by 2018-08-29, http://php.net/manual/en/function.mysql-query.php

3. PHP: mysql_set_charset - Manual, Last Modified, Accessed by 2018-08-29, http://php.net/manual/en/function.mysql-set-charset.php

4. Welcome to The Apache Software Foundation!, Last Modified, Accessed by 2018-08-29, https://www.apache.org/

5. About the Apache HTTP Server Project - The Apache HTTP Server Project, Last Modified, Accessed by 2018-08-29, http://httpd.apache.org/ABOUT_APACHE.html

 

Editor: 도도는 도도의 초록누리의 에디터이다. 샵인클루드 족이다.

반응형
728x90
300x250
[PC활용] APMSetup 6 - 설치

 

이번에 소개할 것은 윈도우 환경에서 Apache + PHP + MySQL을 사용할 수 있는 프로그램이다.

비록 오래되긴 했지만 유용하다.

APMSetup 프로젝트에 대해서 내가 알고 있는 것은 꽤 오래된 프로젝트로 알고 있다.

 

한참 전성기에는 홈페이지도 구축했던 프로젝트로 기억한다.

몇 년전에는 웹서버를 운영한다는 것은 정말 어려운 일이었다. 지금은 조금만 하면 가벼운 웹사이트는 오픈소스 등을 활용하여 쉽게 구축할 수 있다.

 

웹 사이트를 구축하기 위해서는 제일 중요한 것으로 웹 서버가 존재해야 한다.

 

사용 프로그램(Using Program): 윈도우 10, APMSetup 6

 


1. APMSetup 6

 

http://kldp.net/apmsetup 사이트에 접속한다.

Release를 누른다.

 

 

 

그림 1-1. APMSetup 설치, 도도(Dodo)

 

2907-APMSETUP6_2009041200을 기준으로 소개하겠다.

 

그림 1-2. APMSETUP 6 다운로드 받기, Kldp

 

지금 보는 이러한 사이트를 미러링 사이트 또는 파일 사이트라고 한다.

anonymous(익명의 계정)으로 ftp 사이트 등을 열어놓는 곳들이 있다.

물론 이건 FTP 사이트는 아니고, 웹 사이트이다.

 

 

그림 1-3. APMSETUP 6 다운로드 받기, Kldp

 

 

 

그림 1-4. APMSETUP 6 언어팩 설정

 

 

 

그림 1-5. APMSETUP 6 설치, 도도(Dodo)

 

설치 마법사가 뜨면 "다음 >" 버튼을 누른다.

 

 

그림 1-6. APMSETUP 6 설치, 도도(Dodo)

 

실제 프로그램은 APMSETUP6이 아니고, Apache, MySQL, PHP 따로 존재한다.

 

 

그림 1-7. APMSETUP 6 설치, 도도(Dodo)

 

광고 등의 메시지가 나온다.

선택사항이다. 다음을 누른다.

 

 

 

그림 1-8. APMSETUP 6 설치, 도도(Dodo)

 

APM_Setup 6 for Win 32(required)는 필수적으로 체크되어있다.

CUBRID 2008은 CURRID라는 오픈소스 데이터베이스가 있다.

이의 사항을 확인하고 다음을 누른다.

 

 

 

 

그림 1-7. APMSETUP 6 설치, 도도(Dodo)

 

설치 경로를 선택하고 설치를 누른다.

 

 

그림 1-8. APMSETUP 6 설치마법사, 도도(Dodo)

 

APMSetup의 설치 진행 마법사가 진행된다.

지금 장면은 PhpMyAdmin(오픈소스: PHP기반)도 함께 설치되는 모습이다.

 

 

그림 1-9. 방화벽 동의, 도도(Dodo)

 

웹 서버를 사용하면, 포트 등으로 인해 방화벽 해제를 요구한다.

확인을 누른다.

localhost에서 사용할 경우에도 방화벽 해제는 중요하다.

 

 

그림 1-10. 설치 완료, 도도(Dodo)

 

설치가 완료되었다.

아래의 하단에 보면 APMSetup의 아이콘이 뜬다.

녹색으로 된 아이콘에 마우스를 오른쪽 버튼을 누르면 아래와 같이 메뉴가 뜨는 것을 확인할 수 있다.

 

 

그림 1-11. 하단 아이콘 (APMSetup), 도도(Dodo)

 

그림 1-12. APMSetup 6 프로그램

 

그림 1-12는 APMSetup 6이라는 프로그램을 말한다.

Apache Foundation의 Apache 2.2, Oracle의 MySQL Community Edition, PHP.net의 php 5.2가 담겨져 있다.

 

이 프로그램들을 관리하고 실행하는데 도와주는 프로그램이 APMSetup이다.

물론 APMSetup 6이 없어도 Apache 2.2, MySQL, PHP 5.2는 각각 독립적으로 실행이 가능하다.

 

 

 

그림 1-13. Localhost 웹 사이트, 도도(Dodo)

 


2. 참고자료(Reference)

 

1. "/apmsetup/release", kldp, https://kldp.net/apmsetup/release/, Accessed by 2018-08-05

반응형
728x90
300x250

[Ubuntu 12.10] 우분투에 아파치 + 톰켓 설치하기

 

우분투에 아파치 및 톰켓을 설치하는 방법에 대해서 소개하고자 한다.

 


1. 설치에 필요한 큰 과정은 다음의 순서로 진행된다.

    a) Sun Java6이 먼저 깔려있어야 한다.

    b) Apache2를 설치한다.

    c) Tomcat을 설치한다.

    d) Mod_jk를 설치한다.

 

2. 환경설정은 다음과 같이 진행된다.

   a) 아파치 환경 가상호스트 설정하기(Tomcat 지원할 수 있도록 하기 위함이다.)

   b) Tomcat폴더에 있는 Server.xml의 ajp13사용할 수 있도록 설정하기

 

3. 서버 재시작

 

(참고) Tomcat의 루트 변경

 


1. 설치


a) Sun Java6 이 먼저 깔려 있어야 한다.

#sudo apt-get install python-software-properties

#sudo apt-get update

#sudo apt-get install sun-java6*

 

#java -version (설치 확인)

 

b) Apache2를 설치한다.

#sudo apt-get install apache2

아파치 설치 후 `http://서버 아이피` 입력하면 It works 페이지가 나온다.

* 실제 홈페이지 경로 : /var/www (webRoot 디렉토리)

 

c) Tomcat을 설치한다.

#sudo apt-get install tomcat6

톰캣 설치 후 `http://서버 아아피:8080` 입력하면 It works! 페이지가 나온다.

* 실제 기본 디렉토리 /var/lib/tomcat6/webapps/ROOT/

 

d) Mod_jk를 설치한다.

#sudo apt-get install libapache2-mod-jk

 

Mod_jk 환경설정 파일 위치 : /etc/libapache2-mod-jk/workers-properties

 

핵심 설정(중간에 위치한 내용들)

workers.java_home=자바 설치 경로

workers.list=ajp13_worker

workers.ajp13_worker.port=8009

workers.ajp13_worker.host=localhost

workers.ajp13_worker.type=ajp13

workers.ajp13_worker.lbfactor=1

 

* Apache 연동 설정(안해도 됨)

 

파일 위치 : /etc/apache2/mods-available/jk.conf

<IfModule mod_jk.c>

 

    JkWorkersFile /etc/libapache2-mod-jk/workers-properties

    JkLogFile /var/log/apahce2/mod_jk.log

    JkLogLevel Info

    JkOptions +ForwardURlCompatUnparsed

</IfModule>

 


2. 환경설정


a) 아파치 환경 가상호스트 설정하기

#sudo vim /etc/apache2/sites-enabled/000-default

 

#DocumentRoot /var/www/ (주석처리)

DocumentRoot /var/lib/tomcat6/webapps/ROOT/

 

## -- Tomcat에서 처리하는 확장자 패턴 ##

JkMount /*.jsp ajp13_worker

JkMount /*.do ajp13_worker

JkMount /servlet/* ajp13_worker

JkMount /*.lnj ajp13_worker

JkMount /*.xlnj ajp13_worker

 

b) Tomcat폴더에 있는 Server.xml의 ajp13사용할 수 있도록 설정하기

#sudo vim /etc/tomcat6/server.xml

 

<!-- Define an AJP 1.3 Connector on port 8009 -->

(주석 제거)

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

 


3. 서버 재시작

#sudo /etc/init.d/tomcat6 restart

#sudo /etc/init.d/apache2 restart

 

(참고) Tomcat의 루트 변경

#sudo vim /etc/tomcat6/server.xml

 

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

<Host name="localhost" appBase="/var/www"

       unpackWARs="true" autoDeploy="true"

       xmlVaildation="false" xmlNamespaceAware="false">

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

반응형
728x90
300x250

[Linux] Apache 2.2 - VirtualHost 사용 방법

 

 


서버에
IP
주소가 한개 있고, DNS에서 여러 주소(CNAMES) 컴퓨터를 가리킨다. 컴퓨터에서 www.example.com www.example.org 웹서버를 실행하고 싶다.

Note

아파치 서버에 가상호스트 설정을 한다고 호스트명에 대한 DNS 항목이 자동이로 생성되지 않는다. 반드시 DNS IP 주소를 가리키는 이름이 있어야 한다. 안그러면 아무도 웹사이트를 없다. 검사해보기 위해 hosts 파일에 항목을 추가할 있지만, 이는 hosts 항목을 가진 컴퓨터에만 반영된다.



서버
설정

# 아파치가 포트 80 기다린다
Listen 80

#
모든 IP 주소에서 가상호스트 요청을 기다린다
NameVirtualHost *:80

<VirtualHost *:80>
DocumentRoot /www/example1
ServerName www.example.com

#
다른 지시어들도 있다

</VirtualHost>

<VirtualHost *:80>
DocumentRoot /www/example2
ServerName www.example.org

#
다른 지시어들도 있다

</VirtualHost>



별표는
모든 주소를 가리키므로, 주서버는 어떤 요청도 서비스하지 않는다. www.example.com 설정파일에 처음으로 나오므로 가장 높은 우선순위를 가지며, 기본 혹은 초기 서버가 된다. 어떤 ServerName 지시어에도 해당되지않는 요청은 첫번째 VirtualHost 서비스한다.

주의

원한다면 * 대신 시스템의 실제 IP 주소를 사용할 있다. 경우 VirtualHost 아규먼트는 NameVirtualHost 아규먼트와 일치해야 한다:

NameVirtualHost 172.20.30.40

<VirtualHost 172.20.30.40>
#
생략 ...

그러나 ISP에서 동적으로 IP 주소를 가져오는 IP 주소를 모르는 경우에는 * 사용하는 것이 유용하다. * 모든 IP 주소에 해당하므로, IP 주소가 변경되어도 설정을 변경할 필요가 없다. 거의 대부분의 이름기반 가상호스트 설정은 위와 같다. 예외는 다른 IP 주소나 포트로 다른 내용을 서비스하려는 경우이다.

 

 

주의

여기서 설명한 방법은 IP 주소가 몇개라도 적용가능하다.

 

서버는 IP 주소가 두개있다. 하나에서 (172.20.30.40) "" 서버 server.domain.com 서비스하고, 다른 하나에서 (172.20.30.50) 여러 가상호스트를 서비스할 것이다.

서버 설정

Listen 80

# 172.20.30.40
에서 실행하는 ""
서버이다
ServerName server.domain.com
DocumentRoot /www/mainserver

#
다른 주소다
NameVirtualHost 172.20.30.50

<VirtualHost 172.20.30.50>
DocumentRoot /www/example1
ServerName www.example.com

#
다른 지시어들도 있다 ...

</VirtualHost>

<VirtualHost 172.20.30.50>
DocumentRoot /www/example2
ServerName www.example.org

#
다른 지시어들도 있다 ...

</VirtualHost>


172.20.30.50
아닌 주소에 대한 요청은 주서버가 서비스한다. 호스트명 없이, Host: 헤더없이 172.20.30.50 요청하면 www.example.com 서비스한다.


서버
컴퓨터에 IP 주소가 두개 (192.168.1.1 172.20.30.40) 있다. 컴퓨터는 내부 (인트라넷) 네트웍과 외부 (인터넷) 네트웍 사이에 위치한다. 네트웍 밖에서 server.example.com 외부 주소를 (172.20.30.40) 의미하고, 네트웍 내부에서 같은 이름을 내부 주소로 (192.168.1.1) 사용한다.

서버는 VirtualHost 섹션 한개로 내부와 외부 응답에 같은 내용을 서비스할 있다.

서버 설정

NameVirtualHost 192.168.1.1
NameVirtualHost 172.20.30.40

<VirtualHost 192.168.1.1 172.20.30.40>
DocumentRoot /www/server1
ServerName server.example.com
ServerAlias server
</VirtualHost>


이제
네트웍에서 들어온 요청을 같은 VirtualHost에서 서비스한다.

주의:

내부 네트웍에서는 완전한 호스트명 server.example.com 대신 이름 server 가능하다.

또한 위의 예에서 IP 주소 대신 * 사용하여 서버가 모든 주소에 동일하게 동작할 있다.


 



같은
IP
여러 포트에서 서로 다른 도메인을 서비스한다고 가정하자. 이는 "NameVirtualHost" 태그에 포트를 정의하면 가능하다. NameVirtualHost name:port없이 <VirtualHost name:port> 혹은 Listen 지시어만 사용하면 안된다.

서버 설정

Listen 80
Listen 8080

NameVirtualHost 172.20.30.40:80
NameVirtualHost 172.20.30.40:8080

<VirtualHost 172.20.30.40:80>
ServerName www.example.com
DocumentRoot /www/domain-80
</VirtualHost>

<VirtualHost 172.20.30.40:8080>
ServerName www.example.com
DocumentRoot /www/domain-8080
</VirtualHost>

<VirtualHost 172.20.30.40:80>
ServerName www.example.org
DocumentRoot /www/otherdomain-80
</VirtualHost>

<VirtualHost 172.20.30.40:8080>
ServerName www.example.org
DocumentRoot /www/otherdomain-8080
</VirtualHost>

 



서버는
각각 www.example.com www.example.org 해당하는 IP 주소를 (172.20.30.40 172.20.30.50) 가진다.

서버 설정

Listen 80

<VirtualHost 172.20.30.40>
DocumentRoot /www/example1
ServerName www.example.com
</VirtualHost>

<VirtualHost 172.20.30.50>
DocumentRoot /www/example2
ServerName www.example.org
</VirtualHost>




<VirtualHost>
지시어로 지정한 주소에 해당하지않는 주소로 (예를 들어, localhost) 요청이 들어오면 주서버가 있는 경우 주서버가 서비스한다.

 


서버는
각각 www.example.com www.example.org 해당하는 IP 주소를 (172.20.30.40 172.20.30.50) 가진다. IP 80번과 8080 포트에 가상호스트를 돌린다.

서버 설정

Listen 172.20.30.40:80
Listen 172.20.30.40:8080
Listen 172.20.30.50:80
Listen 172.20.30.50:8080

<VirtualHost 172.20.30.40:80>
DocumentRoot /www/example1-80
ServerName www.example.com
</VirtualHost>

<VirtualHost 172.20.30.40:8080>
DocumentRoot /www/example1-8080
ServerName www.example.com
</VirtualHost>

<VirtualHost 172.20.30.50:80>
DocumentRoot /www/example2-80
ServerName www.example.org
</VirtualHost>

<VirtualHost 172.20.30.50:8080>
DocumentRoot /www/example2-8080
ServerName www.example.org
</VirtualHost>


 

이름기반과 IP기반이 혼합된 가상호스트

 

주소중 몇몇은 이름기반 가상호스트로, 다른 것은 IP기반 가상호스트로 서비스하고 싶다.


서버 설정

Listen 80

NameVirtualHost 172.20.30.40

<VirtualHost 172.20.30.40>
DocumentRoot /www/example1
ServerName www.example.com
</VirtualHost>

<VirtualHost 172.20.30.40>
DocumentRoot /www/example2
ServerName www.example.org
</VirtualHost>

<VirtualHost 172.20.30.40>
DocumentRoot /www/example3
ServerName www.example3.net
</VirtualHost>

# IP-
기반
<VirtualHost 172.20.30.50>
DocumentRoot /www/example4
ServerName www.example4.edu
</VirtualHost>

<VirtualHost 172.20.30.60>
DocumentRoot /www/example5
ServerName www.example5.gov
</VirtualHost>


 





모든
포트에 대한 _default_ 가상호스트

어떤 가상호스트에도 해당하지않은 IP 주소와 포트에 대한 모든 요청을 처리하기.

서버 설정

<VirtualHost _default_:*>
DocumentRoot /www/default
</VirtualHost>



default(
기본) 가상호스트의 포트로 와일드카드를 사용하여 어떤 요청도 주서버로 못가도록 만든다.

default 가상호스트는 절대로 이름기반 가상호스트가 사용하는 주소/포트로의 요청을 서비스하지 않는다. 없거나 Host: 헤더가 생략된 요청은 항상 최초의 이름기반 가상호스트(설정파일에서 주소/포트가 처음으로 나온 가상호스트) 서비스한다.

AliasMatch RewriteRule 사용하여 어떤 요청을 특정 페이지(혹은 스크립트) 재작성할(rewrite) 있다.




위의
경우와 같지만, 서버는 여러 포트를 기다리고 80 포트에 대해서 추가로 _default_ 가상호스트를 사용하고 싶다.

서버 설정

<VirtualHost _default_:80>
DocumentRoot /www/default80
# ...
</VirtualHost>

<VirtualHost _default_:*>
DocumentRoot /www/default
# ...
</VirtualHost>



80
포트에 대한 default 가상호스트는 (반드시 와일드카드 포트를 가진 기본 가상호스트 이전에 나와야 한다) 지정하지않은 IP 주소로 보내진 모든 요청을 서비스한다. 주서버는 절대로 요청을 서비스하지 못한다.

 

80 포트에 대해서만 default 가상호스트를 만들고 싶다.

서버 설정

<VirtualHost _default_:80>
DocumentRoot /www/default
...
</VirtualHost>



포트
80
번에 지정하지않은 주소에 대한 요청은 기본 가상호스트가 서비스하고, 다른 지정하지않은 주소와 포트를 가진 요청은 서버가 서비스한다.

 

 

(이름기반 첫번째 예에서) 호스트명 www.example.org 대한 이름기반 가상호스트는 자신의 IP 주소를 가져야 한다. 이름기반 가상호스트의 이전 IP 주소를 캐싱하는 네임서버나 프록시와의 문제를 피하기위해 옮기는 동안 모두를 서비스하고 싶다.

방법은 VirtualHost 지시어에 IP 주소만을 (172.20.30.50) 추가하면되므로 쉽다.

서버 설정

Listen 80
ServerName www.example.com
DocumentRoot /www/example
NameVirtualHost 172.20.30.40

<VirtualHost 172.20.30.40 172.20.30.50>
DocumentRoot /www/example2
ServerName www.example.org
# ...
</VirtualHost>

<VirtualHost 172.20.30.40>
DocumentRoot /www/example3
ServerName www.example.net
ServerAlias *.example.net
# ...
</VirtualHost>



이제
(IP기반 가상호스트를 통한) 새로운 주소와 (이름기반 가상호스트를 통한) 이전 주소 모두 가상호스트에 접근할 있다.

ServerPath 지시어 사용하기

이름기반 가상호스트를 가진 서버가 있다. 올바른 가상호스트를 선택하기위해 클라이언트는 올바른 Host: 헤더를 보내야 한다. 오래된 HTTP/1.0 클라이언트가 헤더를 보내지 못하면 아파치는 클라이언트가 어떤 가상호스트를 보려고하는지 없다 (그래서 최초의 가상호스트가 요청을 서비스한다). 오래된 브라우저와 가능한 호환을 유지하기위해 최초의 가상호스트를 만들고, 여기에 이름기반 가상호스트의 URL 접두사를 포함하는 링크 목록 페이지를 둔다.

서버 설정

NameVirtualHost 172.20.30.40

<VirtualHost 172.20.30.40>
# primary vhost
DocumentRoot /www/subdomain
RewriteEngine On
RewriteRule ^/.* /www/subdomain/index.html
# ...
</VirtualHost>

<VirtualHost 172.20.30.40>
DocumentRoot /www/subdomain/sub1
ServerName www.sub1.domain.tld
ServerPath /sub1/
RewriteEngine On
RewriteRule ^(/sub1/.*) /www/subdomain$1
# ...
</VirtualHost>

<VirtualHost 172.20.30.40>
DocumentRoot /www/subdomain/sub2
ServerName www.sub2.domain.tld
ServerPath /sub2/
RewriteEngine On
RewriteRule ^(/sub2/.*) /www/subdomain$1
# ...
</VirtualHost>




ServerPath
지시어때문에 URL http://www.sub1.domain.tld/sub1/ 대한 요청은 항상 subl-가상호스트가 서비스한다.
클라이언트가 올바른 Host: 헤더를 보낸다면, URL http://www.sub1.domain.tld/ 대한 요청은 subl-가상호스트에서만 서비스한다. 만약 Host: 헤더를 보내지않으면 클라이언트는 최초의 호스트에 있는 정보페이지를 보게된다.

여기에 문제가 있음을 주의하라: 클라이언트가 Host: 헤더를 보내지않으면 http://www.sub2.domain.tld/sub1/ 대한 요청도 subl-가상호스트가 서비스한다.

RewriteRule 지시어를 사용하여 올바른 Host: 헤더를 보내는 클라이언트는 (예를 들어, URL 전치사가 있거나 없는) URL 모두 사용할 있다.

반응형
728x90
300x250

[Linux] Apache 2.x 서버에서 (98)Address already in use: make_sock 오류가 발생할 때 대처방법

(98)Address already in use: make_sock: could not bind to address [::]:80
no listening sockets available, shutting down
unable to open logs

문제 원인 : logs 폴더에 있는 make_sock이라는 파일이 없거나 80포트 영역을 사용하고 있을 때 발생.

해결 방법 :
1. logs 폴더에 make_sock 파일이 정상적으로 있는지 확인.
-> 없다면 재설치하세요.
2. 80포트 사용여부 확인

반응형
728x90
300x250

[Linux] Apache 2.x에서 Host를 Redirect하여 연결하기

호스트를 원하는 방향으로 다시 향할 수 있도록 하는 방법에 대해서 몇가지 소개하고자 합니다.

1. 첫페이지(index)를 통해 방향을 재탐색하는 방법입니다.

<META HTTP-EQUIV="Refresh" Content="0; URL=http://www.company.com/dir1/">

<html>
<head>
<META HTTP-EQUIV="Refresh" Content="3; URL=http://www.company.com/dir1/">
</head>
<body>
This page will forward to http://www.company.com/dir1/ in three seconds.
<p>
Please update your links.
</body>
</html>


2. cgi를 이용한 재 탐색 방법입니다. (mod-cgi)

설정 파일 명 : httpd.conf

ScriptAlias / /var/www/cgi-bin/redirect-script/

설정 파일 명 : /var/www/cgi-bin/redirect-script
#!/usr/bin/perl

print "Status: 301 Moved\r\n" .
      "Location: http://www.new-domain.com/\r\n" .
      "\r\n";

또는

#!/usr/bin/perl -w
use strict;
use CGI qw/:standard/;
print redirect('http://www.new-domain.com');

3. PHP를 이용한 방법 입니다.

<?php
header("Location: http://www.new-domain.com/");
?>

4. 자바스크립트를 이용한 방법입니다.

<html>
<head>
<script language="Javascript" type="text/javascript">
<!-- Hide script
//<![CDATA[
window.location.href="http://www.new-domain.com/"    
//]]> End script hiding -->
</script>
</head>
</html>

5. Apache 서버 모듈을 이용하는 방법입니다.(mod_rewrite)
RewriteEngine On
RewriteRule /.* http://www.new-domain.com/ [R]

6. Apache 서버 모듈을 이용하는 방법입니다.(mod_alias)

설정 파일명 : httpd.conf
1. Redirect Domain:
Redirect / http://www.new-domain.com/
또는
Redirect permanent / http://www.new-domain.com/

2. Redirect Page:
Redirect /web-page.html http://www.new-domain.com/destination-web-page.html

참고 : 재탐색(Redirect)을 지시하기 전에 앞서 Alias(별명)과 ScriptAlias를 지시해야 합니다.
다른 "재탐색(Redirect) 옵션들 포함 : 
(임시 오류 번호 : 302는 기본값 - 임시적으로 재탐색되는 상태)
'오류 번호 303'이라고도 불리고 See other라는 메시지로 보여지는 것은 다른 것과 대체하던지, '오류 번호 404' 를 영구히 제거해야만 합니다.

예제) vhost를 이용한 redirect(재탐색) 기법
<VirtualHost XXX.XXX.XXX.XXX>
ServerName directtolinux.com
ServerAlias www.directtolinux.com
ServerAlias direct-to-linux.com
ServerAlias www.direct-to-linux.com
ServerAlias digitalpenguins.com
ServerAlias www.digitalpenguins.com
Redirect permanent / http://www.yolinux.com/
</VirtualHost>

7. Apache 서버에서 .htaccess 파일을 이용하여 재탐색하기.

vi나 vim에디터를 이용하여 사용자 계정 public_html(사용자 홈페이지 디렉토리 - 설정하신 폴더에 넣어주세요)에 .htaccess를 수정하거나 만듭니다.

RewriteEngine on
RewriteCond %{HTTP_HOST} ^yolinux.com
RewriteRule ^(.*)$ http://www.yolinux.com/$1 [R=permanent,L]

SP. Apache 서버에서 httpd.conf와 .htaccess 파일을 이용한 재탐색 기법.

설정 파일명(위치) : /etc/httpd/conf/httpd.conf
(apache서버를 설치한 폴더 위치를 찾아서 하시기 바랍니다.)

아래의 예제는 현재 .htaccess를 사용할 수 없는 상태를 뜻하고 있습니다.
<Directory />
         AllowOverride None
</Directory>

AllowOverride All로 바꾸시면 .htaccess를 사용하실 수 있습니다.

설정 파일명(위치) : .htaccess(/home/domain/public_html/.htaccess)에 위치합니다.
/home/domain/public_html/.htaccess 에 생성하신 도메인이 아래와 같이 보이는 것과 같이 향하게 됩니다.

특정 도메인으로 이동

Redirect 301 /  http://www.new-domain.com/

특정 파일로 이동

Redirect 301 /old-page-1.html  http://www.newdomain.com/new-page-1.html
Redirect 301 /old-page-2.html  http://www.newdomain.com/new-page-2.html

반응형

+ Recent posts