반응형

 개발을 하다 보면 종종 운영에서 엑셀 다운로드 기능에 대한 요구가 있는데, 이번에 Java에서 엑셀 다운로드를 구현하는 방법에 대해 알아보겠습니다.
 Java에서 엑셀  다운로드 기능을 위해 자주 쓰이는 방식으로 JXLS 라이브러리를 이용한 방식과 POI 라이브러리를 이용한 방식이 있습니다. ( ‘제이엑셀’, ‘포이’ 라고들 읽죠 ㅎㅎ)
 
 
1.  JXLS 라이브러리를 이용한 엑셀 다운로드 기능 구현 방법
2.  티몬에서 JXLS를 이용했을 때의 장애발생 및 해결방안
3.  POI 라이브러리의 SXSSF를 이용한 엑셀 다운로드 기능 구현 방법


 

 

1. JXLS 라이브러리를 이용한 엑셀 다운로드

1-1) JXLS 이란 ?

-  JXLS은 개발자가 미리 만들어 놓은 엑셀 템플릿 파일을 토대로 데이터가 자동으로 쓰여지기 때문에 개발이 굉장히 용이한 방법입니다. JXLS 라이브러리에서 제공해주는 명령어들을 이용해 엑셀 템플릿 파일을 만들고 자바단의 모델 속성명과 엑셀 템플릿 파일내의 속성명을 일치 시켜주면, 엑셀에 모델속성들이 반복적으로 쓰여집니다.
-  JXLS은 데이터를 메모리에 계속 들고 있기 때문에 엑셀에 삽입할 데이터가 많으면 속도가 점점 저하되고, 서버에서 Out Of Memory 에러가 발생할 수 있습니다.

1-2) JXLS 구현방법

  1) 엑셀 템플릿 파일 생성


-  엑셀을 열고 새 통합문서에서 위와 같은 방식으로 메모를 추가하여 명령어를 입력하고, 각 셀에도 명령어를 입력합니다.

-  jx:area(lastCell=”셀위치”) : 명령어 영역의 마지막 셀을 나타냅니다. 즉, 위 사진에서는 B2셀까지만 명령어 영역이라는 것을 지정하는 것입니다. 만약, last Cell 이외의 곳(ex : C2셀)에서 명령어를 입력해도 명령이 실행되지 않습니다.

-  items=”컨텍스트 명 : 자바단에서 생성한 컬렉션 데이터를 갖는 컨텍스트 변수입니다. 자바단에서 JXLS을 위한 Context를 세팅할 때, context명과 items명을 반드시 맞추어야 정상적으로 동작합니다. 아래 소스에서 한번 더 짚고 넘어가겠습니다.

-  var=”모델명 : 각 셀에서 ‘$’문자와 함께 사용할 모델명입니다. 여기서의 모델명은 자바단과 상관없이 아무렇게나 지정해도 상관없고 엑셀의 각 셀에만 이 모델명을 제대로 입력하면 됩니다. 예를 들어, var=”tmon”으로 할 경우, 각 셀에 ${tmon.속성명}으로 작성하면 됩니다.

-  엑셀파일을 만들고 프로젝트내 적절한 위치에 저장합니다. (예제에서는 src/main/resource/template/excel 디렉토리 저장)

-  JXLS 참고 : http://jxls.sourceforge.net/reference/each_command.html

2) 엑셀 다운로드 수행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void jxlsExcelDownloadTest(HttpServletResponse response) throws Exception {
    // 1. 데이터 생성 (예제를 위한 dummy 데이터 생성)
    List<ExcelDownloadModel> excelDataList = getExcelDownloadData();
 
    // 2. 미리 만들어둔 엑셀 템플릿 파일에 대한 InputStream 생성
    InputStream templateStream = resourceLoader.getResource("classpath:template/excel/jxslExcelTemplate.xlsx").getInputStream();
 
    // 3. 응답객체로부터 OutPutStream 생성
    OutputStream targetStream = response.getOutputStream();
 
    // 4. 컨텍스트 객체 생성 및 세팅
    // 4-1. context 속성에 컨텍스트명과 엑셀에 쓰일 데이터를 Key & Value로 세팅
    // 4-2. 여기서 contextName("excelDataList")은 엑셀 템플릿파일에서 items="컨텍스트명"과 반드시 일치
    Context context = new Context();
    context.putVar("excelDataList", excelDataList);
 
    // 5. 엑셀파일로 다운로드위한 response 객체 세팅
    response.setContentType("application/msexcel");
    response.setHeader("Content-Disposition"String.format("attachment; filename=\"%s\"", URLEncoder.encode("downloadFileName.xlsx","UTF-8")));
 
    // 6. templateStream 으로 부터 템플릿을 읽어 들인 후 context를 targetStream에 씀
    JxlsHelper.getInstance().processTemplate(templateStream, targetStream, context);
cs

 

-  위 소스코드에 주석으로 설명을 달아놨지만, 한번 더 짚고 가자면 “4-2”주석부분에 쓰여있는 것처럼, context.putVar(“컨텍스트명”, 데이터컬렉션); 이 부분의 컨텍스트명과 엑셀 템플릿 파일의 items=”컨텍스트명”을 꼭 일치시켜야 합니다. 

-  이 글을 쓰면서 다시 한 번 JXLS을 구현해보는데, 분명히 다 제대로 했는데 정상동작하지 않길래 삽질을 조금 했는데, 엑셀템플릿파일에서 메모에 명령어를 입력할 때, 복&붙으로 넣었는데 큰따옴표 encoding이 깨져서 정상 동작하지 않았습니다. 겉보기엔 큰따옴표가 제대로 들어간 것처럼 보였는데 말이죠... 정상동작하지 않는 경우에 한번 체크해보면 좋을 것 같습니다.


3) 결과

-  A, B셀에는 데이터가 제대로 들어갔지만, (1)의 과정에서 last Cell=”B2”로 지정했기 때문에 C2셀에 명령이 있어도 수행되지 않은 것을 볼 수 있습니다. 


 

 

2. 티몬에서 JXLS을 이용했을 때의 장애발생경험

2-1) 상황

 티몬에서 프로젝트를 진행할 때 요구사항에 어드민 페이지에서 데이터를 엑셀로 다운받을 수 있게 해달라는 내용이 있었습니다. JXLS POI가 있다는 것은 알았지만, 두 방식의 차이를 자세히 모르고 개발기간을 맞추기 위해 비교적 개발이 편리한 JXLS을 선택하여 개발했습니다. 그리고 엑셀 다운로드가 제대로 동작하는지만 테스트하고 운영에 배포가 되었는데 처음에는 별 문제가 없는 것처럼 보였습니다. 그런데 운영에서 데이터가 계속 쌓이자 엑셀 다운로드가 너무 느리다는 이야기가 나왔고, 재연을 해보려는 순간 서버가 다운 되어버렸습니다

2-2) 원인

 
이 프로젝트가 신규프로젝트였기 때문에 데이터가 전혀 없는 상황이었고 개발 테스트를 할 때 고작 dummy데이터를 몇개 집어넣고 테스트를 해본 것이 실수였습니다.
 서버가 다운된 원인을 분석해보니, 다운로드하려는 엑셀 데이터가 약 1만 row가 넘자 Out Of Memory에러로 인해 서버가 다운된 것이었습니다.

2-3) 해결방안

 엑셀 다운로드 하는 방법에 대해 다시 조사를 해보았고, POI라이브러리 중 SXSSF방식이 있다는 것을 알았습니다. 아래에서 자세하게 설명하겠지만, SXSSF방식은 메모리에 있는 데이터를 디스크에 임시 파일로 옮기면서 처리하기 때문에 메모리를 적게 잡아먹는 방식입니다. 메모리에 데이터를 다 들고 있지 않아도 파일다운로드가 되기 때문에 SXSSF방식으로 변경함과 동시에 DB에서 데이터를 가져올 때 paging 처리를 하여 이 장애를 해결했습니다.


 

 

3. SXSSF 라이브러리를 이용한 엑셀 다운로드

3-1) POI ?

-  POI 라이브러리는 HSSF, XSSF, SXSSF방식으로 나누어져 있고 MultiSheet, CellStyle 등을 쉽게 구현할 수 있지만, 자바단에서 ExcelRow생성, 각 Cell에 값 주입 등을 일일이 해줘야 하는 번거로움이 있습니다. 

    1) HSSF : EXCEL 2007 이전 버전(.xls)에서 사용하는 방식
    2) XSSF : EXCEL 2007 이후 버전(2007포함 .xlsx)에서 사용하는 방식
    3) SXSSF : XSSF의 Streaming Version으로 메모리를 적게 사용하여 대용량 엑셀 다운로드에 주로 사용되는 방식

 3-2) SXSSF ? 메모리 ?

-  SXSSF방식을 사용하면 MsOffice 2007 OOXML 형태로 된 파일(.xml)을 디스크(서버)에 생성하여, 데이터를 메모리에 계속 가지고 있지 않고 임시로 이 파일에 기록한 후 메모리를 비워내는 방식으로 메모리를 적게 잡아먹도록 하는 것입니다. 또한 임시로 저장된 파일(.xml)도 지워주어야 디스크 용량이 낭비되지 않을 텐데, 이 임시파일을 지워주는 역할을 메소드도 제공되고 있습니다. 아래 구현방법에서 자세히 설명하겠습니다.

3-3) 구현방법

0) 엑셀 템플릿 파일 생성 (굳이 생성하지 않아도 됩니다)

-  이번에는 JXLS과는 다르게 단순히 모델 속성명만 적어놓은 템플릿입니다. 템플릿을 사용하지 않고, 빈 엑셀문서를 생성한 후 동적으로 모델속성명을 셀에 생성할 수도 있습니다. 예제에서는 모델 속성이 3개 뿐이지만, 실제 운영에서는 많게는 20~30개의 속성이 존재하기 때문에 티몬에서는 위와 같이 미리 템플릿을 만들어두고 사용하고 있습니다.


1) SXSSF Workbook 
생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
// 1. SXSSF WorkBook 생성
public void initExcelTemplate(String templateFileName) throws IOException {
    InputStream templateFile = resourceLoader.getResource("classpath:template/excel/" + templateFileName).getInputStream();
 
    // 엑셀템플릿파일 지정 (지정안하고 빈 통합문서로도 가능)
    XSSFWorkbook xssfWorkbook = new XSSFWorkbook(templateFile);
 
    // 엑셀템플릿파일에 쓰여질 부분 검색
    Sheet originSheet = xssfWorkbook.getSheetAt(FIRST_SHEET_INDEX);
    rowNo = originSheet.getLastRowNum();
 
    // SXSSF 생성
    sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook, ROW_ACCESS_WINDOW_SIZE);
    sheet = sxssfWorkbook.getSheetAt(FIRST_SHEET_INDEX);
cs

-  위에서 미리 만들어둔 엑셀 템플릿파일로부터 SXSSF Workbook을 생성합니다.

-  엑셀템플릿파일로부터 SXSSF Workbook을 생성하기 위해서는 XSSF를 이용하여 workbook을 만들고, SXSSF Workbook을 생성할 때 파라미터로 넣어주면 됩니다. 생성자의 두번째 매개변수는 디스크로 flush되기 전까지 메모리에 들고있는 행의 개수를 뜻합니다.
 
-  참고 : https://poi.apache.org/apidocs/org/apache/poi/xssf/streaming/SXSSFWorkbook.html#SXSSFWorkbook-org.apache.poi.xssf.usermodel.XSSFWorkbook-int-


2) 엑셀에 데이터 삽입 및 메모리 flush

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 2. 엑셀파일에 데이터 삽입
public void addRowDataTest(List<ExcelDownloadModel> excelDataList) throws IOException {
    // 엑셀 row 생성
    for(ExcelDownloadModel model : excelDataList) {
        Row row = sheet.createRow(++rowNo);
 
        // 엑셀 cell 생성 및 값 주입
        Cell cell = row.createCell(0);
        cell.setCellValue(model.getModelSeqno());
        cell = row.createCell(1);
        cell.setCellValue(model.getModelTitle());
        cell = row.createCell(2);
        cell.setCellValue(model.getModelContents());
    }
 
    // 디스크로 flush
    ((SXSSFSheet)sheet).flushRows(excelDataList.size());
cs

-  데이터를 엑셀 파일에 삽입하는 과정입니다. 위에서 언급했듯이 POI 라이브러리는 JXLS와는 다르게 row를 생성하고 각 셀에 일일이 데이터를 넣어주어야 합니다. (편의를 위해 Cell에 삽입하는 소스를 풀어서 작성하였습니다.) 여기서 중요한 부분은 마지막 라인의 flushRows메소드 입니다. flushRows 메소드를 통해 메모리에 있는 데이터를 디스크(임시파일)로 옮기고 메모리를 비워내는 것입니다.


- 참고 : https://poi.apache.org/apidocs/org/apache/poi/xssf/streaming/SXSSFSheet.html#flushRows-int-

3) 엑셀파일 전송 및 임시파일 삭제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 2. 엑셀파일에 데이터 삽입
public void addRowDataTest(List<ExcelDownloadModel> excelDataList) throws IOException {
    // 엑셀 row 생성
    for(ExcelDownloadModel model : excelDataList) {
        Row row = sheet.createRow(++rowNo);
 
        // 엑셀 cell 생성 및 값 주입
        Cell cell = row.createCell(0);
        cell.setCellValue(model.getModelSeqno());
        cell = row.createCell(1);
        cell.setCellValue(model.getModelTitle());
        cell = row.createCell(2);
        cell.setCellValue(model.getModelContents());
    }
 
    // 디스크로 flush
    ((SXSSFSheet)sheet).flushRows(excelDataList.size());
cs

-  dispose 는 (2)번 과정에서 디스크에 임시로 저장해 두었던 파일을 삭제하는 메소드입니다. dispose메소드가 정상적으로 호출되지 않으면 디스크에 임시파일이 그대로 남아있게 되기때문에 flushRows메소드와 한쌍으로 사용하면 됩니다.

-  참고 : https://poi.apache.org/apidocs/org/apache/poi/xssf/streaming/SXSSFWorkbook.html#dispose--

4) 결과

-  JXLS에서는 데이터를 제대로 넣었음에도 불구하고 엑셀 템플릿파일에서 last Cell을 잘못지정하면 데이터가 나오지 않는 것을 확인했고, 그 외의 명령어들을 엑셀 템플릿에 잘못 입력하게 되면 정상동작하지 않을 수 있기 때문에 종종 원인을 파악할 때 삽질을 하게 되는 경우가 있을 수 있습니다. ${excelData.modelSeqno}를 입력하는 과정에서 철자를 틀리게 되면 동작하지 않을 수 있습니다. 

-  반면 SXSSF에서는 자바단에서 모두 컨트롤하기 때문에 엑셀 템플릿파일과는 상관없이 제대로 엑셀에 제대로 데이터가 삽입되지 않을 경우 디버깅이 비교적 쉽습니다.


 

 

4. JXLS  SXSSF 테스트

 -  엑셀 다운로드 Row : 약 1만 건
 -  각 Row의 Column : 29 개
 -  JXLS 방식 10번 수행, SXSSF 10번 수행
 -  결과

JXLS

SXSSF

14.274 sec

4.281 sec

11.25 sec

2.024 sec

10.259 sec

1.793 sec

11.683 sec

1.816 sec

9.681 sec

1.403 sec

8.737 sec

2.074 sec

9.906 sec

2.486 sec

9.221 sec

1.619 sec

9.706 sec

1.657 sec

8.788 sec

1.82 sec

10.35 sec

2.097 sec

- JXLS 평균 수행시간 : 10.35 sec
- SXSSF 평균 수행시간 : 2.097 sec


마치며

 엑셀 다운로드 기능을 개발하는 방법은 여러가지 방법이 있으나 자주 사용되는 방법에 대해 알아보았습니다. 본인이 속한 팀에 따라 엑셀 다운로드 기능을 빈번하게 개발해야 할 수도 있고, 전혀 사용하지 않을 수도 있으나 통상적으로 굉장히 많이 사용되는 기능이 아니기 때문에 공유하면 좋을 것 같아 작성해보았습니다. 감사합니다.

JXLS -

Each-Command Introduction Each-Command is used to iterate through a collection and clone the command XLS area. It is an analogue of Java for operator. Command Attributes Each-Command has the following attributes var is a name of the variable in Jxls context to put each new collection item when itera

jxls.sourceforge.net

 

SXSSFSheet (POI API Documentation)

Field Summary Fields inherited from interface org.apache.poi.ss.usermodel. Sheet BottomMargin , FooterMargin , HeaderMargin , LeftMargin , PANE_LOWER_LEFT , PANE_LOWER_RIGHT , PANE_UPPER_LEFT , PANE_UPPER_RIGHT , RightMargin , TopMargin Constructor Summary Constructors Constructor and Descripti

poi.apache.org

[출처]  Java 대용량 엑셀 다운로드 기능 구현 

'JAVA > Java' 카테고리의 다른 글

Maven Scope 정리  (0) 2019.10.15
Intellij 설정파일  (0) 2019.09.24
Intellij Git 쉽게 사용하기!!!  (0) 2019.08.16
Intellij 알아두면 좋은 단축키  (0) 2019.08.13
Java Stream GroupBy  (0) 2019.08.02
반응형

Intellij UI 간단히 살펴보기

  1. 프로젝트 목록 및 파일상태 확인 창
    1. 파란색 파일명 : 수정된 사항 있음
    2. 초록색 파일명 : 신규 추가
    3. 빨간색 파일명 :
      1. 신규 파일 추가 시 – unstage 상태
      2. merge 등의 작업 후 – 충돌 상태
  2. gulp task 실행 창
  3. 에디터 창
  4. git > network 를 보여주는 log 창
  5. Terminal 입력 창
  6. git > branch 리스트 및 관련 기능 창

로컬저장소(Local Repository) 생성

기존의 저장소에서 clone 받기

웹스톰 설치 후 최초 실행인 경우

1. check out from Version Control > Git  선택

2. Clone Repository 창이 뜨면 해당하는 내용을 입력하고 Clone버튼을 클릭 합니다.
– Clone Repository URL : 해당 프로젝트의 remote repository url 입력

– Parent Directory : 프로젝트를 클론 받을 로컬 경로, 우측 …버튼을 클릭하면 디렉토리 선택 가능
– Directory Name : Parent Directory에 생성되는 프로젝트 폴더 명 (대부분 프로젝트명이 자동으로 입력 됨)

웹스톰 실행 후 프로젝트가 오픈된 상태에서 clone 하는 경우

1. 웹스톰 메뉴 > VCS > Checkout from Version Control > Git 선택

2. Clone Repository 창이 뜨면 해당하는 내용을 입력하고 Clone버튼을 클릭 합니다. (최초 실행과 동일)

로컬 프로젝트를 git 저장소로 변환하기

로컬에서 작업하고 있던 프로젝트를 git remote repository 와 연결할 수 있습니다.

1. VCS > Enable Version Control Integration 선택

2. Select a version control system에서 git 선택 > OK 클릭

3. 여기까지는 terminal 에서 git init 명령어를 입력한 상태와 동일하며, Remote Repository와 연결해주어야 합니다.

웹스톰 메뉴의 VCS > Git > Remotes 클릭

4. Git Remotes 창이 나오면 하단에 + 버튼을 클릭
5. Define Remote 창이 나오면 URL에 Remote Repository URL 을 입력 하고 OK를 클릭

6. Git remotes 창에 Remote Repository URL이 추가되었습니다.

7.하단에 OK를 클릭 하면 리모트 저장소와 연결됩니다.

Add

파일을 stage 에 추가하려면 project > file 선택 > 마우스 우클릭 메뉴 > Git > Add를 선택합니다.
파일 선택 후 단축키를 눌러 추가하거나 폴더를 선택하여 여러개를 한번에 추가할 수 있습니다.

  • mac : opt + cmd + a
  • win : ctrl + alt + a

Commit / Push

웹스톰에서는 Commit 과 Push가 모두 한 창에서 이루어집니다.
웹스톰 메뉴의 VCS > Commit Changes나 단축키를 눌러 Commit Cahnges 창을 엽니다.

  • commit 단축키
    • mac : cmd + k
    • win : ctrl + k
  • push 단축키
    • mac : cmd + shift + k
    • win : ctrl + shift + k

Commit Changes 창 살펴보기

  1. 터미널에서 git status 를 입력했을때의 상태와 동일한 내용이 보여지는 영역입니다
  2. Amend commit, Sign-off commit 등 옵션을 선택할 수 있고, 커밋 전과 후 작업을 추가적으로 선택 가능 합니다
  3. commit message 입력. 우측에 
     아이콘을 누르면 이전의 커밋 히스토리를 볼 수 있습니다
  4. 파일 변경사항을 (Diff)를 비교하며 볼 수 있습니다
  5. 커밋만 할것인지, 커밋 후 푸시할것인지 등 명령을 선택할 수 있습니다

2번 옵션 설정창에서 Check TODO (Show All) 에 체크되어 있으면 <!–[Todo] –> 로 표기해놓은 주석을 체크합니다. Perform code analysis 에 체크되어 있으면 코드의 유효성을 검사하게 되며 Commit 버튼 클릭 후 아래와 같은 확인창을 띄워줍니다.

유효성 검사한 내용을 확인하려면 Review 를 코드가 문제 없다면 Commit 을 선택하면 됩니다. Perform code analysis 를 체크해제 하면 위 확인창도 나오지 않습니다. 위 두가지 사항을 체크 해제하고 사용하면 더욱 빠르게 Commit, Push를 할 수 있으나 코드 및 Todo 검사가 필요하신 분은 체크하고 사용하시기 바랍니다!

Push 창 살펴보기

Push 창에서는 Commit 되는 내용들과 파일들을 확인할 수 있습니다.
우측 영역의 

 아이콘을 클릭하면 변경된 내용의 Diff를 확인할 수 있습니다.

Branch

Branch 목록, 생성, 이동, Merge 등은 모두 Git Branches 창에서 이루어집니다. VCS > Git > Branches , 단축키 등으로 오픈할 수 있습니다. 웹스톰 화면 우측 하단에 

부분은 현재 Branch의 위치를 나타내며, 이 부분을 클릭해도 Git Branches 창을 열 수 있습니다.

Branch 생성

Git Branches > New Branch를 클릭하면 새로운 Branch를 생성 합니다.

Create New Branch 창에서 브랜치명을 입력하고 OK를 클릭 합니다.

웹스톰에서는 브랜치 생성과 동시에 해당 브랜치로 checkout 됩니다.

만약 로컬에서 작업하는 도중 기존 브랜치에서 작업중이라는걸 알게 되었다면 바로 웹스톰에서 +New Branch를 추가해보세요! 작업하던 내용 그대로 새로운 브랜치에서 시작할 수 있습니다.

Branch 이동

Git Branches 창 > Local Branches 에서 원하는 브랜치를 선택 > checkout 클릭시 이동 됩니다.
그밖에 브랜치 관리에 필요한 여러가지 기능이 있습니다.

  • Checkout ad New Branch : 현재 브랜치의 내용 그대로 신규 브랜치 생성
  • Compare : 현재 브랜치와 선택 브랜치의 log 및 Diff 비교
  • Rename : 브랜치명 변경
  • Delete : 브랜치 삭제

Remote branch 목록 업데이트

VCS > Git > Fetch 를 실행하면 원격 브랜치 목록을 다운로드 할 수 있으며,
Branch > Checkout 하면 로컬에서 해당 브랜치로 이동할 수 있습니다.

Version Control

Log

웹스톰 하단 Version control > Log 탭을 보면 gitlab의 Network와 동일한 Log를 확인할 수 있습니다.

아이콘이 현재 나의 HEAD 위치를 알려주는 표시입니다.

console

웹스톰에서 실행되는 모든 git 명령어 들은 console 창에서 실행됩니다.
Terminal 에서 실행하는 명령어들과 비교해서 공부해보시면 좋을것 같습니다!

Event Log

웹스톰 창 우측 하단에 Event Log 버튼을 클릭하면 로컬에서 작업한 내용들을 모두 확인할 수 있습니다.

Merge

Merge 도 Git Branches 창에서 간편하게 처리할 수 있습니다.
Merge가 필요한 브랜치로 이동 > 가져올 내용이 있는 브랜치 > Merge 옵션을 선택합니다.

Merge 가 성공하면 아래와 같은 메시지를 확인할 수 있습니다.

Fast-forword Merge

Git Branches 창에서 실행되는 Merge는 우선적으로 Fast-forward Merge 를 해줍니다.
Fast-foward Merge를 하게 되면 로그의 모양이 일렬로 쌓이게 되고 어떤 브랜치에서 Merge 했는지 직관적으로 볼 수 없어서 선호하지 않은 분들도 있습니다.

Non-fast-forward Merge

웹스톰에서 자동으로 fast-forward Merge를 안하려면 다른 방법으로 Merge 해주어야 합니다.VCS > Git > Merge Changes > Merge Branches  창을 엽니다. Branches to merge 에서 가져올 내용이 있는 브랜치를 선택 > 아래 옵션에서 No fast forward 옵션을 선택 > Merge를 클릭 합니다.

Log 창을 확인해 보면 꺽인 모양으로 로그가 쌓인것을 확인할 수 있습니다.

충돌 (Conflict) 해결

Merge 등의 작업에서 충돌이 발생하면 Files Merged width Conflicts 창이 자동으로 열립니다.
파일명을 더블클릭 하거나 우측에 Merge... 버튼을 클릭하면 비교(Diff) 화면이 열립니다.

비교화면은 3개로 분리되어 있으며, 각 창이 의미하는 내용은 아래와 같습니다.

  • Local Changes : 현재 브랜치의 수정된 내용
  • Changes from Server : 가져올 내용이 있는 브랜치의 내용
  • Result : Local Changes 의 변경사항과 Changes from Server 내용이 조합되어 최종 결과가 만들어지는 창

Local Changes, Changes from Server 의 변경사항을 >>, << 을 눌러 추가하거나 X 를 눌러 제거해 줍니다. 

Result 창에서 직접 코드를 입력하여 수정할 수도 있습니다.

적절하게 수정되었다면 하단에 Apply 버튼을 클릭하여 Merge 시켜 줍니다.
Merge가 완료되면 왼쪽 아래에서 관련 메시지를 확인할 수 있습니다.

Reset

Revert로 되돌리기

파일을 새로 생성하여 Add 했거나 불필요하게 변경된 파일을 변경전 상태로 되돌릴때 Revert를 사용하면 간단하게 되돌릴 수 있습니다.

  1. 해당파일 > 마우스 우클릭 > Git > Revert 를 클릭합니다.
  2. Revert Changes 창에서 파일은 체크해주고 Revert 버튼을 클릭하면 Add 전 상태 (빨간색 파일명)으로 돌아갑니다.
  3. 이때 Delete local copies of added files를 체크하면 선택된 파일은 로컬 디렉토리에서 삭제됩니다.

Commit 되돌리기

Commit한 내용은 Version Control > Log 탭에서 확인할 수 있으며, Log탭에서 Reset 이 가능합니다.
돌아가려는 Commit 지점을 선택 > 마우스 우클릭 > Reset Current Branch to Hear... 메뉴를 선택합니다.

Git Reset 창에서 reset 관련 옵션을 선택할 수 있습니다.

  • Soft : 커밋 삭제 / 작업내용, Index(stage) 유지
  • Mixed : 커밋, Index(stage) 삭제 / 작업내용 유지. [기본옵션]
  • Hard : 커밋, 작업내용, Index(stage) 모두 제거. 한순간에 모두 잃을 수 있으니 신중하게 선택 필요!

만약 로컬에서 변경한 내용이 있는 상태에서 Reset 을 실행했을때 충돌이 일어나면 Git Reset Problem 창이 실행됩니다.
Hard Reset  (로컬 내용을 삭제하고 리셋), Dont' Reset  (리셋 중지), Smart Reset 할건지 선택 할 수 있습니다.

Smart Reset 을 선택하면 충돌을 해결하기 위해 Files Merged width Conflicts 창이 나타납니다.
충돌(Conflict)해결과 동일한 스텝으로 스마트하게 해결하면 됩니다!

Stash

작업 중간에 급하게 수정해야 하는 상황이 생긴다면 작업내용을 stash 하여 임시저장할 수 있습니다.
VCS > Git > Stash Changes 를 선택하면 Stash 창이 열립니다.

Message 에 적절하게 제목을 입력해 주고 Create Stash 를 클릭합니다.
이렇게 저장된 내용은 VCS > Git > UnStash Changes 창을 통해서 확인할 수 있습니다.

  • View : Stash 내용 보기
  • Drop : 선택된 Stash 삭제
  • Clear : Stash 리스트 전체 삭제

원하는 Stash list 를 선택하고 Apply Stash를 클릭하면 에디터로 내용이 들어오게 됩니다.
옵션중에 Pop stash 를 체크하게 되면 Apply Stash 버튼이 Pop Stash 버튼으로 변경되며, 선택했던 Stash는 Stash list에서 삭제됩니다.

As new branch 에 브랜치명을 입력하면 선택한 stash의 내용과 함께 새로운 브랜치를 생성하며, 해당 브랜치로 checkout 됩니다.

Cherry-Pick

Version Control > Log 화면에서 다른 브랜치의 Commit을 Cherry-Pick 으로 쉽게 가져올 수 있습니다.

Cherry-Pick을 실행하면 Commit Changes 창이 열립니다. 적절한 Commit Message를 입력한 후 Commit 해줍니다.

Cherry-Pick이 성공하면 아래와 같은 메시지를 확인할 수 있습니다.

History

Local history

웹스톰에서는 로컬에서 작업한 히스토리를 파일선택 > 마우스 우클릭 > Local History > Show History  메뉴에서 확인할 수 있습니다.

기존에 작성 했던 내용을 확인하거나 그 내용을 현재(Current) 작업중인 내용에 추가할 수도 있습니다.

Git history

파일이 열린 상태에서 왼쪽 줄번호 클릭 > 마우스 우클릭 > Annotate를 클릭하면 git에 해당 코드를 추가한 사람을 추적할 수 있습니다.

마우스를 올리면 툴팁으로 조금 더 자세한 내용을 볼 수 있습니다.

파일명 > 마우스 우클릭 > Git > Compater width.. 메뉴를 선택하면 File Revisions 창에서 해당 파일의 commit history를 더 자세하게 살펴볼 수 있습니다.

 

Gulp Tasks

Gulp Tasks 창은 터미널에서 명령어 입력 없이 ‘더블클릭’ 으로 간편하게 task를 실행할 수 있습니다.

만약 gulp task창이 안보인다면 gulpfile.js 파일을 클릭 > 마우스 우클릭 > Show Gulp Tasks 를 클릭 하면 나타납니다.

 

출처: https://wit.nts-corp.com/2017/04/12/4399

'JAVA > Java' 카테고리의 다른 글

Intellij 설정파일  (0) 2019.09.24
JXLS, POI JAVA에서 Excel 사용하는 구현 방법 및 종류 비교  (0) 2019.08.19
Intellij 알아두면 좋은 단축키  (0) 2019.08.13
Java Stream GroupBy  (0) 2019.08.02
POJO 란  (0) 2019.07.16
반응형

  • Main menu | Navigate | Next Highlighted Error - 에러를 찾아갈때 참 용이하게 사용된다.

  • Version Control Systems | Next Change - Local Changes 가 일어날 경우 소스 수정된 부분을 찾을때 용이함.

  • Editor Actions | Duplicate Entire Lines - 소스 복붙할땐 이거만한 기능이 없다.

  • Main menu | Code | Surround With... - if..else , try.. catch - 구분을 자동으로 만들어 주는 단축키. 필수!!

  • Main menu | Refactor | Extract | Method... - Method로 변경해주는 단축키 

'JAVA > Java' 카테고리의 다른 글

JXLS, POI JAVA에서 Excel 사용하는 구현 방법 및 종류 비교  (0) 2019.08.19
Intellij Git 쉽게 사용하기!!!  (0) 2019.08.16
Java Stream GroupBy  (0) 2019.08.02
POJO 란  (0) 2019.07.16
Intellij 사용 플러그인  (0) 2019.07.16
반응형
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.tistory.ddoriya.stream;
 
import com.tistory.ddoriya.stream.model.User;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
 
public class TestSteam2 {
 
    public static void main(String[] args) {
 
        List<User> userList = Arrays.asList(
                new User("1""AAA""11""123@mail"),
                new User("2""BBB""11""123@mail"),
                new User("3""BBB""11""123@mail"),
                new User("4""BBB""11""123@mail"),
                new User("5""BBB""11""123@mail"),
                new User("6""CCC""11""123@mail"),
                new User("7""CCC""11""123@mail"),
                new User("8""CCC""11""123@mail"),
                new User("9""CCC""11""123@mail"),
                new User("10""CCC""11""123@mail")
        );
 
        List<String> stringList = new ArrayList<>();
 
 
        Map<String, Long> test = userList.stream().collect(
                Collectors.groupingBy(User::getName, Collectors.counting())
        );
 
        System.out.println(test.get("AA"));
        System.out.println(test);
 
        Map<String, List<User>> test1 = userList.stream().collect(
                Collectors.groupingBy(User::getName)
        );
 
        System.out.println(test1.get("AAA").size());
        System.out.println(test1.get("BBB").size());
        System.out.println(test1.get("CCC").size());
 
        System.out.println(test1.get("AAA").get(0));
        System.out.println(test1);
 
        for (User user : userList) {
//            System.out.println(user.toString());
        }
 
    }
}
cs

'JAVA > Java' 카테고리의 다른 글

Intellij Git 쉽게 사용하기!!!  (0) 2019.08.16
Intellij 알아두면 좋은 단축키  (0) 2019.08.13
POJO 란  (0) 2019.07.16
Intellij 사용 플러그인  (0) 2019.07.16
java Stream 사용법  (0) 2019.07.11
반응형

스프링 책을 보다보면 POJO 기반의 구성이 특징이라는 내용을 볼 수 있다.

 

하지만 책의 내용만으로는 POJO가 무엇인지 당췌 와닿지 않아서 열심히 구글링을 해보고

 

책 보다 훨씬 이해 잘되는 포스팅을 찾아 정리해보려 한다.

 

출처는 http://m.blog.naver.com/weekamp/186678831   헬리코님의 블로그이다.

 


 

 

POJO = Java Bean

 

여기서 Java Bean은 Sun의 Java Beans나 EJB의 Bean을 뜻하는 것이 아닌

순수하게 setter, getter 메소드로 이루어진 Value Object성의 Bean을 의미.

 

 

예를 들면 이와 같은 코드이다.

 

 

즉, 이클립스를 통해 자동으로 생성하던 깡통 빈 클래스를 통해서 생성된 객체.

 

그것이 바로 스프링에서 말하는 POJO인 것이다.

 

 

그렇다면 POJO가 스프링의 중요한 특징 중에 하나인 이유는 무엇인가?

 

그것은 클래스 상속을 강제하지 않고, 인터페이스 구현을 강제하지 않으며, 애노테이션 사용을 강제하지 않는다.

 

즉 개발자는 특정한 라이브러리나 컨테이너의 기술에 종속적이지 않고, 가장 일반적인 형태로 코드를 작성할 수 있다는 것이다.

 

 

이것은 생산성에도 유리하고, 코드에 대한 테스트 작업 역시 좀 더 유연하게 할 수 있다는 장점이 생긴다.

 

 


 

POJO가 아닌 대표적인 객체

 

public HelloServlet extends HttpServlet{......}

 

자바 서블릿 코드를 작성할 떄는 이렇게 반드시 HttpServlet을 상속 받아야한다.

 

서블릿 프로그래밍을 한다는 이유로 객체지향 프로그래밍의 핵심적인 기능 중 하나인 상속을 빼앗긴 것이나 마찬가지이다.

 

코드를 작성하고 있는 개발자가 직접 상속을 사용할 수 있는 기회가 없어진것이다.

 

또한 extends HttpServlet이 추가되면서 이 코드를 이해하기 어려워진다.

 

HttpServlet에서 어떤 기능을 제공하는지 어떤 코드를 어떻게 재사용해야 할지 판단하기도 어렵다.

 

'JAVA > Java' 카테고리의 다른 글

Intellij 알아두면 좋은 단축키  (0) 2019.08.13
Java Stream GroupBy  (0) 2019.08.02
Intellij 사용 플러그인  (0) 2019.07.16
java Stream 사용법  (0) 2019.07.11
Intellij Rest Client  (0) 2019.07.11
반응형

 

'JAVA > Java' 카테고리의 다른 글

Java Stream GroupBy  (0) 2019.08.02
POJO 란  (0) 2019.07.16
java Stream 사용법  (0) 2019.07.11
Intellij Rest Client  (0) 2019.07.11
Intellij 셋팅 파일  (0) 2019.07.09
반응형

개인적으로 자바8의 꽃이라 생각하는 스트림 포스팅이다. 내용이 워낙 방대하긴하나 쉽게 생각하면 스트림은 결국 API들의 모임이기때문에 외워야할게 많기도하다.

 

1. 스트림(Stream)

스트림은 자바8에 추가된 API로 자바의 자료구조들을 선언적으로 다루는 역할을 한다. 앞선 함수형 인터페이스 포스팅에서 설명했던 인터페이스들이 엄청나게 등장을 하니 스트림을 다룬다면 외우기 싫어도 외워질수밖에 없을 것이다.

자료구조들을 다루는 역할을 하기때문에 스트림은 배열이나 List처럼 생성한 다음 요소를 추가하는 형태가 아니다. 정적 팩토리 메서드(Static Factory Method)를 이용해 자료구조로부터 생성한다.

int[] numberArr = {1, 2, 3, 4, 5, 6};
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6);
Set<Integer> numberSet = new HashSet<>(numberList);

Arrays.stream(numberArr);
Stream.of(1, 2, 3, 4, 5, 6);
numberList.stream();
numberSet.stream();

 

1-1. 스트림 vs 컬렉션(Collection)

위에서 스트림은 자료구조들을 다룬다고했다. 하지만 이미 자바에서는 각종 자료구조들의 구현체를 손쉽게 사용할 수 있도록 util패키지를 제공하고있다. util패키지에서 제공하는 컬렉션 프레임워크는 자바의 자랑이자 강점이기도한데 무엇이 아쉬워서 스트림이 또 나온걸까? 앞선 표현에 이미 답이 있는데 컬렉션은 자료구조들의 구현체고 스트림은 자료구조들을 다루는 역할을 한다. 컬렉션은 데이터를 담는 것이 제역할이기때문에 제공하는 API들도 데이터를 넣었다 빼는것들이 대부분이다.

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

numbers.add(1);
numbers.get(0);
numbers.remove(1);

그렇기때문에 컬렉션을 이용해 코딩을 할때는 컬렉션에 데이터들을 담고, 그 컬렉션을 순회하고 꺼내면서 직접적으로 연산을 하는것이 주를 이룬다.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenList = new ArrayList<>();

for(int number : numbers){
if(number % 2 == 0){
evenList.add(number);
}
}

System.out.println(evenList);

(numbers 리스트에서 짝수를 추출하는 코드. 리스트는 요소를 추가, 삭제, 순회하는 API만 제공하기때문에 내가 어떻게 짝수를 걸러내야하는지를 짜야된다.)

 

이에반해 스트림은 연속된 자료들을 다루고 연산하는 API를 지원한다. 컬렉션과는 다르게 요소를 추가한다거나 삭제하는건 불가능하다. 연산을 지원하기때문에 짝수만 걸러내는것도 매우 명시적이고 선언형으로 짤 수 있다.

List<Integer> evenList = Stream.iterate(1, n -> n+1)
.limit(6)
.filter(number -> number % 2 == 0)
.collect(toList());

System.out.println(evenList);

아주 간단한 예제다보니 라인수에는 별 차이가 없는것 같지만 내가하고싶은일이 뭔지 명령을 내리는 코드형태이지 그 명령을 어떤식으로 처리하는지가 코드에 나타나진 않는다는 차이점이 있다. 스트림은 내부반복을 지원하기때문에 반복문같은것도 코드에서 전혀 나타나지않는다.

 

연산이 복잡해지면 복잡해질수록 컬렉션을 이용한 코드는 반복문이 계속 등장하고 연산을 저장할 변수들이 나타나면서 뭘 하고싶어하는지 알아보기가 힘들어지게되는 반면 스트림을 이용하면 코드량과 가독성을 한번에 취할 수 있게되는 것이다.

 

1-2. 중간 연산, 최종 연산

위에 짧은 예제코드를 보면 알 수 있겠지만 스트림을 사용한 코드는 계속해서 도트(Dot) 연산자로 메서드 체이닝(Method Chaining)을 일으킨다. 초보개발자의 경우 도트가 연속해서 나오면 코드를 이해하기 어려워하는 경우도 많은데 저런 경우는 한가지만 명확히 생각하면 된다. 메서드 체이닝 중간에 있는 메서드는 절대 void 형태가 아니며 메서드가 반환하는 어떤 객체가 도트 뒤에 나오는 메서드를 갖고있다고 보면 되는것이다.

/* limit() 메서드는 무언가 객체를 반환할것이다.
void타입의 메서드라면 filter() 메서드를 호출할 수가 없기때문이다.
limit()이 반환하는 객체는 filter() 메서드를 갖고있는 객체다.
그리고 filter() 메서드역시 collect() 라는 메서드를 갖고있는 객체를 반환한다.
*/
.limit(6)
.filter(number -> number % 2 == 0)
.collect(toList());

limit(), filter(), collect() 같은 메서드들은 전부 스트림에서 제공하는 메서들이다. 결국 스트림 메서드들은 전부 연속해서 스트림을 반환하고 있기때문에 저런 코드가 가능한것이다.

마지막으로 collect() 메서드는 스트림이 아니라 List 타입을 반환하기때문에 List 변수에 무언가 값을 저장하고있는걸 알 수 있는데 스트림 API는 이런식으로 스트림을 반환하는 메서드와 스트림이 아닌 값을 반환하는 메서드로 나뉜다.

그리고 계속해서 스트림을 반환하여 메서드 체이닝의 근간이 되게하는 메서드들을 중간 연산 메서드라 부르고 스트림이 아닌 값을 반환하여 메서드 체이닝을 끊는 메서드를 최종 연산 메서드라 부른다.

이 둘을 이렇게 구분짓는건 생각보다 중요한데, 최종 연산이 존재하지 않으면 중간연산들로만 이루어진 메서드 체이닝은 실행되지 않는다. 이 코드는 아무런 값도 찍히지 않는다.

Stream.iterate(1, n -> n+1)
.limit(6)
.peek(System.out::println) // 스트림을 순회하며 하나씩 요소를 꺼내 출력하는 구문
.filter(number -> number % 2 == 0);

중간 연산이고 최종 연산이고 중간에 출력문을 하나 넣어놨으니 이 코드는 실행하면 출력문이 나와야 될듯 하지만 아무것도 찍히지 않는다. 최종연산이 없기때문에 중간연산만으로는 실행이 되지 않는것이다.

Stream<Integer> stream = Stream.iterate(1, n -> n+1)
.limit(6)
.peek(System.out::println)
.filter(number -> number % 2 == 0);

stream.collect(toList());

이런식으로 최종 연산 메서드가 실행될때 비로소 중간 연산들도 실행이 된다.

 

1-3. Lazy & ShortCircuit

스트림은 게으르(Lazy)다. 결론부터 말하자면 최종연산이 존재하지않으면 중간연산은 실행되지 않는다. 위에서부터 말했지만 스트림은 어디까지나 연산을 위한 객체로 그 자체로 자료구조의 역할을 하지 않는다. 때문에 최종연산이 존재하지않는 스트림은 그 의미가 없다고 볼 수 있다. 스트림이 게으르다는걸 확인할 수 있는 샘플코드는 어렵지않게 짤 수 있다.

 

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s = stream.peek(System.out::println);

peek()메서드는 forEach()처럼 스트림의 요소를 순회하며 소비(Consumer<T>)하는 메서드이다. forEach()와 한가지 다른점은 중간 연산이라는점이다. 때문에 해당코드를 실행하면 우리가 기대하는 출력문은 출력되지 않는다.

 

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s = stream.peek(System.out::println);
s.collect(toList());

최종연산이 호출될때 비로소 중간연산들도 실행되는걸 볼 수 있다.

 

Lazy외에도 스트림은 여러 최적화기법들을 도입했는데 그 중하나가 Short Circuit이다. 이건 뭔말일꼬.. 할 수 있는데 &&(And), ||(Or) 연산을 생각하면 이해가 쉽다.

 

&&연산은 좌항과 우항 모두 true일때 true을 반환하는 연산인데 좌항이 false면 우항은 쳐다도보지않는다.

 

Object obj = null;

boolean b = 1 == 2 && obj.toString().equals(123);

엉성한 코드지만..;; obj가 null이기때문에 거기다가 toString()를 호출하면 NullPointerException이 발생해야한다. 하지만 이미 좌항이 false이기때문에 우항은 실행도 하지않게되고, 그리하여 아무런 예외없이 코드는 실행된다.

이런기법이 Short Circuit이다.

 

List<String> list = Arrays.asList("a", "b", "c");

boolean b = list.stream().allMatch(str -> {
System.out.println(str);
return str.equals("d");
});

스트림 요소를 순회하면서 모든 요소에 Predicate이 true인지를 확인하는 구문이다. 출력된 내용을 보면 a만 찍히는걸 볼 수 있다. 전부다 d여야 true를 반환하는데 처음부터 d가 아니니 연산을 끊고 false를 반환하는 것이다.

 

1-4. 기본형 스트림

숫자타입 리스트를 선언할때는 제네릭을 이런식으로 선언한다.

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

Auto Boxing, Auto Unboxing이 지원되면서 그냥 기본형 쓰듯 사용할 수 있지만 내부적으로까지 boxing 비용이 없어진건 아니다. 하지만 자바의 제네릭은 기본형을 지원하지않기때문에 어쩔수 없이 저렇게 사용할 수 밖에 없다. 스트림 역시 기본형으로 제한을 걸기위해서는 저런식으로 해야한다.

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

스트림은 이런 boxing 비용을 줄이기위해 기본형에 특화된 객체를 따로 제공하고있다.

IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0, 4.0, 5.0);
LongStream longStream = LongStream.of(1L, 2L, 3L, 4L, 5L);

기본형 스트림을 사용하면 boxing비용을 줄일 수 있을 뿐더러 해당 타입에 알맞는 연산들을 메서드로 제공하고있다.

intStream.sum(); // Stream<Integer> 로도 가능하다. 다만 기본메서드로 제공되지는 않는다.

한가지 주의할 점은 스트림과 기본형 스트림은 관계가 없기때문에 단순하게 타입변환이 되지않는다. 그래서 다형성을 이용하기가 어렵다. 물론 그에 대한 API도 제공해주고있다.

//이외에도 mapToObj, mapToDouble 같은 메서드를 제공한다.
List<Integer> interger = intStream.boxed().collect(toList());

적은 범위의 값들을 다룬다면 그냥 Boxing 비용을 감안하고서라도 Stream을 사용하는게 맘편해보이지만 그런 비용을 무시할 수 없을만큼 큰 범위의 데이터라면 기본형 스트림을 고려해보는것이 좋을것같다.

 

1-5. 대표적인 API들

람다는 기존에 없던 문법들이 추가되어 신기한 맘으로 공부해야하지만 사실 스트림은 특별할건 없다. 가볍게본다면 새로운 API가 추가된정도로 볼 수도있다. 개인적으로 스트림을 공부하면서 느낀건 그 철학이나 탄생배경도 중요하지만 일단 실무에서 써먹으려면 API를 달달 외워야 한다는것이다. 대표적인 API들을 소개하면서 포스팅을 마치겠다.

 

1) 중간연산

-Stream<R> map(Function<A, R>)

-Stream<T> filter(Predicate<T>)

-Stream<T> peek(Consumer<T>)

 

2) 최종연산

-R collect(Collector)

-void forEach(Consumer<T>)

-Optional<T> reduce(BinaryOperator<T>)

-boolean allMatch(Predicate<T>)

-boolean anyMath(Predicate<T>)



출처: https://multifrontgarden.tistory.com/128 [우리집앞마당]

'JAVA > Java' 카테고리의 다른 글

POJO 란  (0) 2019.07.16
Intellij 사용 플러그인  (0) 2019.07.16
Intellij Rest Client  (0) 2019.07.11
Intellij 셋팅 파일  (0) 2019.07.09
REST 개념  (0) 2019.03.21
반응형

Intellij Rest Client

 

과거 Restful API 테스트를 해야될 경우 

curl, 또는 브라우저에서 테스트를 종종 하였다...

https://curl.haxx.se/ - 참고

 

하지만 Intellij 사용 중 마음에 드는 기능이 하나 존재하는데

REST Client (https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html)

 

내가 원하는 rest정보를 담아 테스트를 할 수 있다.

'JAVA > Java' 카테고리의 다른 글

Intellij 사용 플러그인  (0) 2019.07.16
java Stream 사용법  (0) 2019.07.11
Intellij 셋팅 파일  (0) 2019.07.09
REST 개념  (0) 2019.03.21
Spring Boot microservice  (0) 2019.03.05
반응형

이똘이 사용하는 셋팅파일입니다.

settings.zip
0.02MB

 

코드 테마

 

keymap - (Eclipse 사용자라면 거부감없이 사용 가능)

 

 

'JAVA > Java' 카테고리의 다른 글

java Stream 사용법  (0) 2019.07.11
Intellij Rest Client  (0) 2019.07.11
REST 개념  (0) 2019.03.21
Spring Boot microservice  (0) 2019.03.05
메서드, 클래스 명명규칙 어떡하면 좋을까?  (0) 2017.07.04
반응형

REST가 무엇인가?

REST는 분산 시스템 설계를 위한 아키텍처 스타일이다.

아키텍처 스타일이라는건 쉽게 말하면 제약 조건의 집합이라고 보면 된다.

RESTful은 무엇인가?

RESTful은 위의 제약 조건의 집합(아키텍처 스타일, 아키텍처 원칙)을 모두 만족하는 것을 의미한다.

REST라는 아키텍처 스타일이 있는거고 RESTful API라는 말은 REST 아키텍처 원칙을 모두 만족하는 API라는 뜻이다.

우리가 REST와 RESTful을 동일한 의미로 사용하곤 하는데 엄격하게는 다르다는 것을 알 수 있다.

->이로써 REST와 RESTful, RESTful API가 무엇인지, 어떻게 다른지를 말할 수 있게 되었다.

REST가 필요한 이유는 뭘까?

1. 위에서 말한 것과 같이 분산 시스템을 위해서다.

거대한 애플리케이션을 모듈, 기능별로 분리하기 쉬워졌다. RESTful API를 서비스하기만 하면 어떤 다른 모듈 또는 애플리케이션들이라도 RESTful API를 통해 상호간에 통신을 할 수 있기 때문이다.

2. WEB브라우저 외의 클라이언트를 위해서다. (멀티 플랫폼)

웹 페이지를 위한 HTML 및 이미지등을 보내던 것과 달리 이제는 데이터만 보내면 여러 클라이언트에서 해당 데이터를 적절히 보여주기만 하면 된다.

예를 들어 모바일 애플리케이션으로 html같은 파일을 보내는 것은 무겁고 브라우저가 모든 앱에 있는 것은 아니기 때문에 알맞지 않았는데 RESTful API를 사용하면서 데이터만 주고 받기 때문에 여러 클라이언트가 자유롭고 부담없이 데이터를 이용할 수 있다.

서버도 요청한 데이터만 깔끔하게 보내주면되기 때문에 가벼워지고 유지보수성도 좋아졌다.

REST의 구성 요소

HTTP URI = 자원

HTTP Method = 행위

MIME Type = 표현 방식

1
2
GET /100 HTTP/1.1
Host : jeong-pro.tistory.com
cs

위와 같은 Request 메세지가 있으면 URI자원은 "/100" 이고, HTTP Method는 "GET" 이다.

MIME 타입은 보통 Response Http header 메세지에 Content-type으로 쓰인다. 여기서는 없다.

그러면 이해하기를 jeong-pro.tistory.com 서버에 /100 이라는 자원을 GET(조회)하고 싶다는 요청으로 해석이 가능하다. 이게 REST 방식을 이용한 Request 예시다. (참고로 이것은 이해를 위한 것일 뿐 RESTful 하다고는 못한다.) 

1
2
3
4
HTTP/1.1 200 OK
Content-Type : application/json-patch+json
 
[{"title""helloworld""author""jeong-pro"}]


이런 Response가 왔다고 해보자.

그러면 Content-Type을 보고 클라이언트는 IANA라는 타입들의 명세를 모아놓은 사이트에 가서 application/json-patch+json 이라는 타입의 명세를 확인하고 아래 Body의 내용이 json타입이구나를 알 수 있는 것이다.


REST는 알겠고 그러면 그 제약 조건이 뭔데요?

1. Client/Server

2. Stateless : 각 요청에 클라이언트의 context가 서버에 저장되어서는 안된다.

3. Cacheable : 클라이언트는 응답을 캐싱할 수 있어야 한다.

4. Layered System : 클라이언트는 서버에 직접 연결되었는지 미들웨어에 연결되었는지 알 필요가 없어야 한다.

5. Code on demand(option) : 서버에서 코드를 클라이언트에게 보내서 실행하게 할 수 있어야 한다.

6. uniform interface : 자원은 유일하게 식별가능해야하고, HTTP Method로 표현을 담아야 하고, 메세지는 스스로를 설명(self-descriptive)해야하고, 하이퍼링크를 통해서 애플리케이션의 상태가 전이(HATEOAS)되어야 한다.

왜 uniform interface에 강조가 되어있냐면, 1~5번의 제약 조건은 HTTP를 기반으로하는 REST는 HTTP에서 이미 충분히 지켜지고 있는 부분이라서 비교적 덜 주의를 기울여도 된다.

RESTful하려면 저 uniform interface를 잘 지켜야 한다.

그 중에서도 HATEOAS와 self-descriptive를 잘 지켜야 한다.

필자가 주로 쓰는 Spring에는 spring-data-rest, spring hateoas, spring-rest-doc으로 두 제약을 지키기위해 사용할 수 있는 라이브러리가 있다. (이 포스트는 면접을 위한 포스트일 뿐 사용법과 테스트는 다른 포스트에서 한다.)

HATEOAS는 Link 라는 HTTP 헤더에 다른 리소스를 가리켜 주는 값을 넣는 방법으로 해결한다.

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Content-Type : application/json
Link : </spring/1>; rel="previous",
        </spring/3>; rel="next";
{
    "title" : "spring의 모든 것"
    "author" : "jeong-pro"
}
cs

위와 같이 해당 정보에서 다른 정보로 넘어갈 수 있는 하이퍼링크를 명시해야 한다는 것이다.

완벽한 REST는 무엇일까? WEB이다.

어떤 Application이 생겼다고 브라우저는 버전을 업데이트할 필요가 없고, 브라우저가 해당 application으로 어떻게 요청하는지를 알게 해야할 필요가 없다.


* 장점

- 메세지를 단순하게 표현할 수 있고 WEB의 원칙인 확장에 유연하다. (멀티플랫폼)

- 별도의 장비나 프로토콜이 필요없이 기존의 HTTP 인프라를 이용할 수 있다. (사용이 용이함)

- server, client를 완전히 독립적으로 구현할 수 있다.

* 단점

- 표준, 스키마가 없다. 결국은 API 문서가 만들어지는 이유다.

- 행위에 대한 메소드가 제한적이다. (GET, POST, PUT, DELETE, HEAD, ...)



* REST는 분산 시스템 설계를 위한 이키텍처 스타일이라고 했다.

마이크로서비스라는 말을 들어보았을 것이다. 이 쪽으로 질문이 연계될 수 있다.

RESTful API를 이용해서 하나의 큰 서비스 애플리케이션을 여러 모듈화된 작은 서비스 애플리케이션(마이크로 서비스)들로 나눌 수 있게 됐기 때문이다.


* REST를 공부하니까 URI와 URL의 차이점에 대해서도 이해할 수 있게되었다.

Uniform Resource Identifier, Uniform Resource Locator

REST에서는 모든 것을 Resource로 표현한다. 그리고 그 자원은 유일한 것을 나타낸다. Identifier, 식별자라는 것이다.

반면에 과거의 웹에서는 Identifier의 개념이 따로 필요없었다. html같은 파일들을 주고 받았기 때문에 파일의 위치를 가리키는 Locator를 썼다고 이해하면 된다.

URI가 파일뿐만 아니라 여러 자원들 까지도 포함하는 개념으로 이해할 수 있다.


* 자세한 명세를 알고 싶은 사람은 마이크로소프트에서 발표한 REST 가이드라인을 보면 좋을 것이다.

https://github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md

참고 사이트

https://www.youtube.com/watch?v=RP_f5dMoHFc

https://spring.io/understanding/REST



출처: https://jeong-pro.tistory.com/180 [기본기를 쌓는 정아마추어 코딩블로그]

'JAVA > Java' 카테고리의 다른 글

Intellij Rest Client  (0) 2019.07.11
Intellij 셋팅 파일  (0) 2019.07.09
Spring Boot microservice  (0) 2019.03.05
메서드, 클래스 명명규칙 어떡하면 좋을까?  (0) 2017.07.04
이클립스 실행 인자 입력 받기  (0) 2016.11.02

+ Recent posts