<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>고코딩</title>
    <link>https://go-coding.tistory.com/</link>
    <description>여러가지 개발 프로그래밍 블로그입니다. 제 블로그 포스팅 내용들이 도움이 되었으면 좋겠습니다.</description>
    <language>ko</language>
    <pubDate>Tue, 9 Jun 2026 13:03:03 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>고코딩</managingEditor>
    <image>
      <title>고코딩</title>
      <url>https://tistory1.daumcdn.net/tistory/4421258/attach/0d5c9e4ad7ff4e2babeba264b9a8a057</url>
      <link>https://go-coding.tistory.com</link>
    </image>
    <item>
      <title>그 동안 소홀했던...</title>
      <link>https://go-coding.tistory.com/115</link>
      <description>&lt;p&gt;22년 말 취업에 성공하고 회사에서 일하면서 여러가지 업무도 배우고 필요한 지식도 쌓고, 놀기도 많이 놀았다.&lt;/p&gt;
&lt;p&gt;23년이 한 달 밖에 안남은 지금 지난 내 1년을 다시 생각해보면 나는 아무것도 안했다.&lt;/p&gt;
&lt;p&gt;물론 회사도 다니고 친구들도 만나고 운동도 하고 할거 다 했지만, 전혀 개발자로서 성장하지 않았다.(이 녀석 전혀 성장하지 않았어...) 슬슬 대부분의 개발자들이 23년 회고록을 작성하고 있을텐데 나는 작성할 만한 회고 내용이 딱히 없다.&lt;/p&gt;
&lt;p&gt;요 몇일동안 갑자기 내가 개발자로서 성장하고 있는가? 라는 생각이 뇌리에 꽃혔고 5년 뒤에 나는 지금 하고 있는 업무를 업무가 돌아가게만 만들어 놓고 만족하고, 임시방편으로 메꾸고 말것 같았다. 최근 주변에 취업한 친구가 있는데, 그 친구의 시작부터 지금까지의 모습을 보았는데 태도에 자극을 많이 받았다. 항상 궁금해하고 열정적이고 배우고 싶어하고 써먹어보고 싶어하는 모습이였다. 초보인 그 친구는 나에게 물어보면서 성장을 하고 있었는데, 처음에는 내가 대답해줄 수 있는 수준의 질문만을 했다. 하지만 먼 미래에는 내가 질문하는 모습이 보이기 시작했다. 그리고 그 친구는 개발자로서 성장한 모습이 너무 확연히 보였다. 유튜브나 블로그에서 결국 성공한 개발자, CTO의 공통점을 보다보면 내 모습에는 없고 그 친구의 모습에서 그 공통점을 찾을 수 있었다. &lt;/p&gt;
&lt;p&gt;요즘 이런 생각이 많이 들게 되니까 지난 1년동안 난 무엇을 한것인가 싶었다. 담당하고 있는 업무 시스템이 너무 구린 버전이라고 변명하면서 고치려는 생각은 하지 않았다. 지금 생각엔 분명히 고치려면 어떻게든 최대한으로 고칠 수 있었다. 그리고 퇴근 이후 시간에 분명히 공부할 수 있었지만, 하루종일 모니터만 보고있어서 눈이 피곤하단 이유로 공부하지 않았다. 그러면서 유튜브, 인스타는 손에서 놓지 않고 있었다.&lt;/p&gt;
&lt;p&gt;어제 개발 유튜브를 보면서 매일 공부를 하시는 CTO님을 보았다. 그분이 하시는 말씀이 열심히 하는게 아니라 매일 하다보면 습관이 만들어진다고 하신다. 뭔가 나도 열심히는 아니지만 매일 공부를 해야겠다는 생각을 했다. 공부가 아니더라도 매일 글을 쓰고, 지식을 습득하고, 코드 한 줄이라도 더 작성하고 싶었다.&lt;/p&gt;
&lt;p&gt;다행히도 지금 회사에서는 워라벨 보장이 잘 되어있다. 바쁠땐 바쁘지만 분명히 내 시간이 충분하다. 이제 부터라도 이 시간을 활용해서 매일 매일 개발자로서 성장하는 습관을 들여야겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;요즘 미래에 대해서 많이 생각하게 되었다. 내가 하고싶은걸 하고 만들고 싶은걸 만드는 미래를 항상 꿈꾸지만 지금 상태로는 먹고사는데 급급하고, 돈 걱정에 하루하루 고민하는 미래가 그려질것 같다. 연차로 따져보면 2년차 개발자이지만 이 상태로라면 10년뒤에도 2년차 개발자로 평가받게 될것 같았다. &lt;/p&gt;
&lt;p&gt;내 분야는 실력이 없다면 의미가 없는 분위기이다. 실제로 실력이 없는 분을 보았고 그 분에 대해서 뒤에서 말하는 내용을 듣다보니 내가 지금에 안주하면 먼 미래에 나도 실력 없는 개발자가 되어 있을게 분명하다.&lt;/p&gt;
&lt;p&gt;이제 부터라도 목표를 세우고 성취감을 느끼면서 성장을 해야겠다.&lt;br&gt;아직 목표는 정하지 않았다. 웹 개발자, 백엔드 개발자, 인프라 관리자 등등 내가 알아야할 정보가 너무 많고, 분명히 프로젝트도 진행해야한다. 빨리 구체적이 목표를 잡고 실행에 옮겨야겠다. 한가지 확실한 것은 무조건 습관을 들여야 살아남을 수 있을것 같다.&lt;/p&gt;
&lt;p&gt;몇몇 멋있는 CTO분들을 보면서 나는 최종적으로 CTO가 되고싶었다. 지금도 그렇다. 어떤 종류의 CTO일지는 모르겠다. 하지만 나는 경영, 마케팅, 기획 쪽에는 별 생각이 없다. 내가 기술적으로 지식적으로 실력이 좋아지고 싶은 생각 뿐이다. &lt;/p&gt;</description>
      <category>회고</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/115</guid>
      <comments>https://go-coding.tistory.com/115#entry115comment</comments>
      <pubDate>Sun, 26 Nov 2023 13:50:45 +0900</pubDate>
    </item>
    <item>
      <title>[백준 4485번] 녹색 옷 입은 애가 젤다지?</title>
      <link>https://go-coding.tistory.com/106</link>
      <description>&lt;h1&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/4485&quot;&gt;백준 4485번&lt;/a&gt;&lt;/h1&gt;
&lt;h3&gt;문제&lt;/h3&gt;
&lt;p&gt;젤다의 전설 게임에서 화폐의 단위는 루피(rupee)다. 그런데 간혹 &amp;#39;도둑루피&amp;#39;라 불리는 검정색 루피도 존재하는데, 이걸 획득하면 오히려 소지한 루피가 감소하게 된다!&lt;/p&gt;
&lt;p&gt;젤다의 전설 시리즈의 주인공, 링크는 지금 도둑루피만 가득한 N x N 크기의 동굴의 제일 왼쪽 위에 있다. [0][0]번 칸이기도 하다. 왜 이런 곳에 들어왔냐고 묻는다면 밖에서 사람들이 자꾸 &amp;quot;젤다의 전설에 나오는 녹색 애가 젤다지?&amp;quot;라고 물어봤기 때문이다. 링크가 녹색 옷을 입은 주인공이고 젤다는 그냥 잡혀있는 공주인데, 게임 타이틀에 젤다가 나와있다고 자꾸 사람들이 이렇게 착각하니까 정신병에 걸릴 위기에 놓인 것이다.&lt;/p&gt;
&lt;p&gt;하여튼 젤다...아니 링크는 이 동굴의 반대편 출구, 제일 오른쪽 아래 칸인 [N-1][N-1]까지 이동해야 한다. 동굴의 각 칸마다 도둑루피가 있는데, 이 칸을 지나면 해당 도둑루피의 크기만큼 소지금을 잃게 된다. 링크는 잃는 금액을 최소로 하여 동굴 건너편까지 이동해야 하며, 한 번에 상하좌우 인접한 곳으로 1칸씩 이동할 수 있다.&lt;/p&gt;
&lt;p&gt;링크가 잃을 수밖에 없는 최소 금액은 얼마일까?&lt;/p&gt;
&lt;h3&gt;입력&lt;/h3&gt;
&lt;p&gt;입력은 여러 개의 테스트 케이스로 이루어져 있다.&lt;/p&gt;
&lt;p&gt;각 테스트 케이스의 첫째 줄에는 동굴의 크기를 나타내는 정수 N이 주어진다. (2 ≤ N ≤ 125) N = 0인 입력이 주어지면 전체 입력이 종료된다.&lt;/p&gt;
&lt;p&gt;이어서 N개의 줄에 걸쳐 동굴의 각 칸에 있는 도둑루피의 크기가 공백으로 구분되어 차례대로 주어진다. 도둑루피의 크기가 k면 이 칸을 지나면 k루피를 잃는다는 뜻이다. 여기서 주어지는 모든 정수는 0 이상 9 이하인 한 자리 수다.&lt;/p&gt;
&lt;h3&gt;출력&lt;/h3&gt;
&lt;p&gt; 테스트 케이스마다 한 줄에 걸쳐 정답을 형식에 맞춰서 출력한다. 형식은 예제 출력을 참고하시오.&lt;/p&gt;
&lt;img width=&quot;683&quot; alt=&quot;image&quot; src=&quot;https://user-images.githubusercontent.com/54675591/170618018-3994b298-886a-4e13-9052-3a54db54efb7.png&quot;&gt;

&lt;hr&gt;
&lt;h3&gt;풀이&lt;/h3&gt;
&lt;p&gt;단순한 다익스트라 알고리즘 문제이다. 우선순위 큐를 이용해서 각 방문한 노드의 위치 값과 가중치 값을 넣어 놓고 그 값을 비교하면서 큐가 빌때까지 넣으면 된다. 항상 다음 노드로 갈때는 이전 값보다 무조건 값이 커지게 되어 있으므로 언젠간 큐가 비게 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Main {
    private static int[] posX = {1,0,-1,0};
    private static int[] posY = {0,1,0,-1};
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;
        StringBuilder sb = new StringBuilder();
        int cnt = 0;
        while(true){
            int n = Integer.parseInt(br.readLine());
            if(n == 0){
                break;
            }
            int[][] map = new int[n][n];
            int[][] cost = new int[n][n];
            for(int i=0;i&amp;lt;n;i++){
                st = new StringTokenizer(br.readLine(), &amp;quot; &amp;quot;);
                for(int j=0;j&amp;lt;n;j++){
                    map[i][j] = Integer.parseInt(st.nextToken());
                    cost[i][j] = Integer.MAX_VALUE;
                }
            }
            dijkstra(map, cost, n);
            System.out.println(&amp;quot;Problem &amp;quot; + ++cnt + &amp;quot;: &amp;quot; + cost[n-1][n-1]);
        }
    }
    public static void dijkstra(int[][] map, int[][] cost, int size){
        PriorityQueue&amp;lt;Node&amp;gt; priorityQueue = new PriorityQueue&amp;lt;&amp;gt;();
        //0,0 넣고 시작하기
        cost[0][0] = map[0][0];
        priorityQueue.add(new Node(0,0,cost));
        while(!priorityQueue.isEmpty()){
            Node cnode = priorityQueue.poll();
            //주변 값 검사
            for(int i=0;i&amp;lt;4;i++){
                int newX = posX[i]+cnode.x;
                int newY = posY[i]+cnode.y;
                if(newX&amp;gt;=0 &amp;amp;&amp;amp; newY &amp;gt;= 0 &amp;amp;&amp;amp; newX&amp;lt;size &amp;amp;&amp;amp; newY&amp;lt;size){
                    //cost값이 -1이면 무조건 넣고 아니면 넣을 값과 넣지 않을 값을 비교해서 작은 값을 넣는다.
                    int newCost = map[newX][newY] + cost[cnode.x][cnode.y];
                    if(newCost &amp;lt; cost[newX][newY]){
                        cost[newX][newY] = newCost;
                        priorityQueue.add(new Node(newX, newY,cost));
                    }
                }
            }
        }

    }
    public static class Node implements Comparable&amp;lt;Node&amp;gt;{
        int x;
        int y;
        int[][] cost;
        public Node(int x, int y, int[][] cost){
            this.x = x;
            this.y = y;
            this.cost = cost;
        }

        @Override
        public int compareTo(Node o) {
            if(this.cost[this.x][this.y]==cost[o.x][o.y]){
                return 0;
            }else if(this.cost[this.x][this.y]&amp;gt;cost[o.x][o.y]){
                return 1;
            }else{
                return 0;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>코딩테스트</category>
      <category>Java</category>
      <category>다익스트라</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/106</guid>
      <comments>https://go-coding.tistory.com/106#entry106comment</comments>
      <pubDate>Fri, 27 May 2022 11:45:30 +0900</pubDate>
    </item>
    <item>
      <title>하이퍼바이저와 컨테이너</title>
      <link>https://go-coding.tistory.com/105</link>
      <description>&lt;h1&gt;가상화 기법 두가지&lt;/h1&gt;
&lt;h3&gt;하이퍼바이저(hypervisor)&lt;/h3&gt;
&lt;p&gt;호스트 시스템 하드웨어를 몇 단계로 제어해 각 게스트 OS에 필요한 리소스를 제공한다. 게스트 머신은 시스템 프로세스로 실행되지만, 하드웨어 리소스에는 가상화를 거쳐 접근한다. 예를 들어, AWS는 오랫동안 오픈 소스인 젠(Xen) 하이퍼바이저 기술을 기반으로 구축해왔다. 그 외 하이퍼바이저 플랫폼으로는 VMWare ESXi, KVM, 마이크로소프트의 Hyper-V등이 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/54675591/164952645-30befb57-e201-4acd-9828-52ccf48640a8.png&quot; alt=&quot;Untitled Diagram drawio&quot;&gt;&lt;/p&gt;
&lt;h3&gt;컨테이너(Container)&lt;/h3&gt;
&lt;p&gt;초경량 가상 서버로, 완전히 독립적인 OS를 실행하는 대신 호스트 OS의 커널을 공유한다. 컨테이너는 텍스트 스크립트로 구축할 수 있으며 몇 초 만에 생성하고 실행해서 네트워크를 통해서 쉽고 안정적으로 공유할 수 있다. 현재 가장 유명한 기술은 도커(Docker)이다. LXC(리눅스 컨테이너 프로젝트)는 도커에 영감을 준 기술이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/54675591/164953055-1083a02a-813d-41e0-bc2b-9c33da985bc9.png&quot; alt=&quot;Untitled Diagram drawio (2)&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;출처 : 모던 리눅스 관리 2장&lt;/p&gt;</description>
      <category>CS지식</category>
      <category>가상화</category>
      <category>컨테이너</category>
      <category>하이버파이저</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/105</guid>
      <comments>https://go-coding.tistory.com/105#entry105comment</comments>
      <pubDate>Sun, 24 Apr 2022 11:12:10 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Java SE, EE, ME의 차이점</title>
      <link>https://go-coding.tistory.com/104</link>
      <description>&lt;h1&gt;[JAVA] Java SE, EE, ME의 차이점&lt;/h1&gt;
&lt;h3&gt;Java SE(Standard Edition)&lt;/h3&gt;
&lt;p&gt;Java SE는 데스크톱 및 서버, 최근의 고사양 임베디드 시스템을 위한 표준 자바 플랫폼으로 표준적인 컴퓨팅 환경을 지원하기 위한 자바 가상 머신 규격 및 API 집합을 포함한다. 따라서 Java EE, Java ME 등 다른 플랫폼은 구체적인 목적에 따라 Java SE를 기반으로 API를 추가하거나 자바 가상 머신 규격 및 API의 일부를 택해서 정의된다.&lt;/p&gt;
&lt;p&gt;Java SE는 Java Software Development Kit(SDK)으로 구현된다.&lt;/p&gt;
&lt;p&gt;대체로 처음에 자바개발에 입문할 때 접한다.&lt;/p&gt;
&lt;h3&gt;Java EE(Enterprise Edition)&lt;/h3&gt;
&lt;p&gt;자바 플랫폼, 엔터프라이즈 에디션(Java EE)는 &lt;strong&gt;자바를 이용한 서버측 개발을 위한 플랫폼이다.&lt;/strong&gt; 엔터프라이즈 환경을 위한 도구로 EJB, JSP, Servlet, JNDI 등을 지원하며 애플리케이션 개발에 주로 사용된다.&lt;/p&gt;
&lt;p&gt;대체로 웹개발자들이 사용하는 배포판이다.&lt;/p&gt;
&lt;h3&gt;Java ME(Micro Edition)&lt;/h3&gt;
&lt;p&gt;Java ME는 제한된 자원을 가진 휴대 전화, PDA, 세트톱박스 등에서 Java 프로그래밍 언어를 지원하기 위해 만들어진 플랫폼 중 하나를 가리킨다.&lt;/p&gt;
&lt;p&gt;Java ME는 개인용 컴퓨터에서 쉽게 에뮬레이트할 수 있고, 쉽게 전송할 수 있다는 이점으로 인해 휴대전화 플랫폼에서 인기가 있었다. 이는 닌텐도나 소니 그리고 마이크로소프트사의 게임 플랫폼들에서 개발할 때 해당 플랫폼 전용의 비싼 개발용 하드웨어와 소프트웨어등의 개발 키트가 필요하다는 점에 비하면 상대적으로 개발 및 테스트 등에서 상당한 장점으로 작용했다.&lt;/p&gt;</description>
      <category>JAVA</category>
      <category>Java</category>
      <category>jdk</category>
      <category>자바 플랫폼</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/104</guid>
      <comments>https://go-coding.tistory.com/104#entry104comment</comments>
      <pubDate>Tue, 1 Feb 2022 15:24:49 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] 단순 계산기 만들기</title>
      <link>https://go-coding.tistory.com/103</link>
      <description>&lt;h1&gt;[Kotlin] 단순 계산기 만들기&lt;/h1&gt;
&lt;p&gt;언어를 가장 빠르게 익히는 방법은 무엇일까? 문법 공부먼저 하는것도 좋지만 바로 뭐라도 만들어보는게 가장 좋다. &lt;/p&gt;
&lt;p&gt;Java만 사용해 오던 나는 최근 Kotlin에 관심을 가지기 시작하였고 코틀린을 공부하기 시작했다. 일단 첫번째는 단순 계산기 만들기!!&lt;/p&gt;
&lt;p&gt;나는 안드로이드 개발자가 아니니 콘솔창에서 숫자 2개와 연산자 하나를 입력 받으면 결과가 출력이 되게 하겠다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;개발환경&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;OS : macOS Big Sur&lt;/p&gt;
&lt;p&gt;IDE : IntelliJ&lt;/p&gt;
&lt;p&gt;Language : Kotlin&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;목적&lt;/h3&gt;
&lt;p&gt;코틀린스럽게 만들기.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;코드&lt;/h3&gt;
&lt;p&gt;생각해야 할것들&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;입력받은 문자를 숫자(Integer)로 변환해주어야 한다.&lt;/li&gt;
&lt;li&gt;연산자에 따른 switch를 해주어야 한다.&lt;/li&gt;
&lt;li&gt;잘못된 입력일때 예외 처리를 해주어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;1. 입력받은 문자를 숫자(Integer)로 변환해주어야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;생각해 보니 코틀린에서 문자열 입력받는 방법도 모른다.... 자바에서는 &lt;code&gt;Scanner&lt;/code&gt;나 &lt;code&gt;StringBuffered&lt;/code&gt; 를 통해서 입력 받았는데... 바로 구글링 해보았다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;readLine()&lt;/code&gt;이라는 함수로 입력을 받는다고 한다. 그럼 이에 String -&amp;gt; Integer 해주어야 하는데 어떻게 해야할까?? 자바에서는 &lt;code&gt;Integer.parseInt()&lt;/code&gt; 로 사용하는데 찾아보니 코틀린도 별 다른게 없다... 코틀린 전용 함수가 있다면 알려주시면 감사합니다.&lt;/p&gt;
&lt;p&gt;그럼 결국 코드는&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;val num1: Int = Integer.parseInt(readLine())
val num2: Int = Integer.parseInt(readLine())&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 된다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 연산자에 따른 switch를 해주어야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;자바에서는 if 또는 switch를 이용해서 구간처리를 해준다. 근데 코틀린에서는 switch대신에 &lt;strong&gt;when&lt;/strong&gt; 이라는 표현식이 있다. 이것을 사용해서 구현해보자. 참고로 표현식이란 단순히 말해서 리턴값이 있다는 거다.&lt;/p&gt;
&lt;p&gt;코드는 아래와 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;val op: String? = readLine()
val result =
    when (op){
    &amp;quot;+&amp;quot; -&amp;gt; num1 + num2
    &amp;quot;-&amp;quot; -&amp;gt; num1 - num2
    &amp;quot;*&amp;quot; -&amp;gt; num1 * num2
    &amp;quot;/&amp;quot; -&amp;gt; num1 / num2
    else -&amp;gt; throw Exception(&amp;quot;op is wrong&amp;quot;)
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 된다.&lt;/p&gt;
&lt;p&gt;여기서 궁금증!!! 잘 보면 num1과 num2의 타입은 &lt;code&gt;Int&lt;/code&gt; 이고 op의 타입은 &lt;code&gt;String?&lt;/code&gt; 인데 왜 op만 null이 가능한 &lt;code&gt;?&lt;/code&gt; 가 있는 것일까??&lt;/p&gt;
&lt;p&gt;힌트는 &lt;code&gt;Integer.parseInt()&lt;/code&gt; 는 자바의 함수이고 &lt;code&gt;readLine()&lt;/code&gt; 은 코틀린 정적 함수이다. 맥 사용자라면 command + B 를 눌러 함수의 구현부로 가보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;//Integer.java의 내용
//...
public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }
//...


//코틀린의 readLine() 함수 객체으 내용
/**
 * Reads a line of input from the standard input stream.
 *
 * @return the line read or `null` if the input stream is redirected to a file and the end of file has been reached.
 */
fun readLine(): String? = LineReader.readLine(System.`in`, Charset.defaultCharset())&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;잘 모르겠으면 각 함수의 리턴 타입을 보자. parseInt()는 리턴 타입이 &lt;code&gt;int&lt;/code&gt;이고 readLine()은 리턴 타입이 &lt;code&gt;String?&lt;/code&gt;으로 구현되어 있다. 그래서 당연히 리턴 타입에 맞춰서 변수의 타입을 선언해주어야 한다. 만약 &lt;code&gt;?&lt;/code&gt;의 의미를 잘 모르면 구글링 해보아요~&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 잘못된 입력일때 예외 처리를 해주어야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;잘못된 입력에는 무엇이 있을까?? &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;숫자를 입력하지 않고 다른 문자를 입력하였을 때&lt;/li&gt;
&lt;li&gt;op에 사칙연산 구문이 아닌 다른 문자를 입력하였을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;두번째 예외는 아까 when()을 구현하면서 else에서 예외 처리를 하게 만들었다. 그럼 첫 번째만 처리하면 된다. 그럼 먼저 어떤 예외가 발생하는지 일단은 아래 코드를 실행해 보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;fun main(args: Array&amp;lt;String&amp;gt;){

    val num1:Int = Integer.parseInt(readLine())
    val op: String? = readLine()
    val num2: Int = Integer.parseInt(readLine())
    val result =
        when(op){
            &amp;quot;+&amp;quot; -&amp;gt; num1 +num2
            &amp;quot;-&amp;quot; -&amp;gt; num1-num2
            &amp;quot;*&amp;quot; -&amp;gt; num1*num2
            &amp;quot;/&amp;quot; -&amp;gt; num1/num2
            else -&amp;gt; throw Exception(&amp;quot;op is wrong&amp;quot;)
        }

    println(&amp;quot;${num1} ${op} ${num2} = ${result}&amp;quot;)

}&lt;/code&gt;&lt;/pre&gt;
&lt;img width=&quot;277&quot; alt=&quot;스크린샷 2022-01-29 오후 5 00 09&quot; src=&quot;https://user-images.githubusercontent.com/54675591/151653041-36541ef7-717d-465d-bea1-7370ce918e43.png&quot;&gt;

&lt;p&gt;정상적으로 입력이 위의 그림과 같은 결과를 얻는다.&lt;/p&gt;
&lt;img width=&quot;791&quot; alt=&quot;스크린샷 2022-01-29 오후 5 03 05&quot; src=&quot;https://user-images.githubusercontent.com/54675591/151653091-3cf7e644-9ba9-4a9a-a762-fe2932f8a5f6.png&quot;&gt;

&lt;p&gt;잘못된 입력시 &lt;code&gt;NumberFormatException&lt;/code&gt; 예외를 리턴해주는 것을 알 수 있다. 그럼 우리는 try catch를 이용해서 예외 처리 하는 구문을 추가하면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;fun main(args: Array&amp;lt;String&amp;gt;){

    val num1:Int = try{
        Integer.parseInt(readLine())
    }catch (e: NumberFormatException){
        println(&amp;quot;잘못된 문자를 입력하였습니다.계산을 종료합니다.&amp;quot;)
        return
    }
    val op: String? = readLine()
    val num2: Int = try{
        Integer.parseInt(readLine())
    }catch (e: NumberFormatException){
        println(&amp;quot;잘못된 문자를 입력하였습니다. 계산을 종료합니다.&amp;quot;)
        return
    }
    val result =
        when(op){
            &amp;quot;+&amp;quot; -&amp;gt; num1 +num2
            &amp;quot;-&amp;quot; -&amp;gt; num1-num2
            &amp;quot;*&amp;quot; -&amp;gt; num1*num2
            &amp;quot;/&amp;quot; -&amp;gt; num1/num2
            else -&amp;gt; throw Exception(&amp;quot;op is wrong&amp;quot;)
        }

    println(&amp;quot;${num1} ${op} ${num2} = ${result}&amp;quot;)

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 코드를 리팩토링 해주면 된다. &lt;/p&gt;
&lt;img width=&quot;275&quot; alt=&quot;스크린샷 2022-01-29 오후 5 31 00&quot; src=&quot;https://user-images.githubusercontent.com/54675591/151653955-89c4b6c7-55b1-4141-a284-e1724d24d9b5.png&quot;&gt;

&lt;p&gt;정상적으로 예외 처리가 되는 것을 볼 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;결론&lt;/h3&gt;
&lt;p&gt;사실 계속해서 입력 받는 처리나 소숫점을 위한 Double형 사용, 코드 함수화 등 여러가지 리팩토링을 해야하지만 뭐 그냥 내가 단순히 코드 익숙해지기 위해서 공부하겠다는 건데 그냥 여기서 끝내겠다. 나중에 코틀린에 대해 좀더 익숙해지면 더 완벽하게 계산기를 만들어 봐야겠다.&lt;/p&gt;</description>
      <category>코틀린</category>
      <category>Java</category>
      <category>kotlin</category>
      <category>계산기</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/103</guid>
      <comments>https://go-coding.tistory.com/103#entry103comment</comments>
      <pubDate>Sat, 29 Jan 2022 17:35:53 +0900</pubDate>
    </item>
    <item>
      <title>TDD와 BDD에서 사용되는 given/when/then 행동과 실습</title>
      <link>https://go-coding.tistory.com/102</link>
      <description>&lt;h1&gt;[Spring Boot] when(), given(), any() 및 BDD 설명&lt;/h1&gt;
&lt;p&gt;패스트 캠퍼스 강의를 듣던 도중 테스트 구문 작성 코드에서 처음 보는 코드를 보았다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;when(this.todoRepository.save(any(TodoEntity.class)))
                .then(AdditionalAnswers.returnsFirstArg()); //TodoRepository가 save()메소드를 호출해서 TodoEntity값을 받으면 받은 엔티티 값을 반환하도록 설정&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;원래 given-when-then 의 구조는 알고 있었지만 저렇게 스태틱 함수로 구현되어 있는 건 처음 봐서 이해가 되지 않았다. 여러가지 서치를 해보고 정리를 해본다.&lt;/p&gt;
&lt;p&gt;용어 정리&lt;/p&gt;
&lt;h3&gt;TDD&lt;/h3&gt;
&lt;p&gt;Test Driven Development의 약자로 &lt;strong&gt;테스트가 개발을 주도한다&lt;/strong&gt;라는 개념으로 사용된다.&lt;/p&gt;
&lt;p&gt;TDD는 테스트를 먼저 만들고 테스트를 통과하기 위한 행동들이 모두 개발을 주도하는 것을 목표로 한다. &lt;/p&gt;
&lt;p&gt;보통 테스트는 개발이 끝난 후에 하는 과정이라고 생각할 수 있다.&lt;/p&gt;
&lt;p&gt;하지만 TDD에서는 테스트 코드를 먼저 만들고 테스트 코드를 통과시키기 위해 개발코드를 만들어가면서 완성해 간다.&lt;/p&gt;
&lt;p&gt;예시를 하나 보자&lt;/p&gt;
&lt;h4&gt;1. Calculator클래스와 CalculatorTest 클래스를 생성한다.&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Calculator{

}
public class CalculatorTest{
  Calculator calc = new Calculator();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2. 내가 원하는 기능(덧셈, plus())이 어떻게 생겼고 뭘 반환하는지 CalculatorTest에 작성한다.&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Calculator{

}
public class CalculatorTest{
  Calculator calc = new Calculator();

  @Test
  void plus(){
    int a = 10;
    int b = 20;
    int result = calc.plus(a,b);
  }
}
//현재 Calculator 클래스에는 plus라는 메서드가 존재하지 않기 때문에 실패하게 된다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3. Calculator 클래스에 해당 메서드를 통과할 수 있는 간단한 클래스를 구현한다.&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Calculator{
  public int plus(int a, int b){
    return 0;
  }
}
public class CalculatorTest{
  Calculator calc = new Calculator();

  @Test
  void plus(){
    int a = 10;
    int b = 20;
    int result = calc.plus(a,b);
  }
}
//Calculator의 plus메서드에서는 어떤 값을 반환해도 좋다 일단 컴파일을 성공시키는 것이 목적이다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4. Calculator클래스에서 어떤 결과를 내어야 할지 CalculatorTest클래스에서 다시 정의한다.&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Calculator{
  public int plus(int a, int b){
    return 0;
  }
}
public class CalculatorTest{
  Calculator calc = new Calculator();

  @Test
  void plus(){
    int a = 10;
    int b = 20;
    int result = calc.plus(a,b);
    assertEquals(result, a+b);
  }
}
//assertEquals는 JUnit 5에 존재하는 검증문으로 매개변수 2개가 동일하면 테스트 성공을 의미한다.
//현재는 메서드가 0을 반환하니 실패한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5. Calculator클래스에서 해당 테스트를 통과할 수 있는 로직을 구현한다.&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Calculator{
  public int plus(int a, int b){
    return a+b;
  }
}
public class CalculatorTest{
  Calculator calc = new Calculator();

  @Test
  void plus(){
    int a = 10;
    int b = 20;
    int result = calc.plus(a,b);
    assertEquals(result, a+b);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;BDD&lt;/h3&gt;
&lt;p&gt;Behavior Driven Development의 약자로 TDD에서 따왔기 때문에 TDD추구하는 가치가 크게 다르지 않다.&lt;/p&gt;
&lt;p&gt;BDD를 처음으로 생각한 Danial Terhorst-North가 TDD를 수행하고 있던 도중 아래와 같은 생각을 했다고 한다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TDD하다가 해당 코드를 분석하기 위해서 많은 코드들을 분석해야하고 복잡성으로 인해 &amp;#39;누군가가 나에게 이 코드는 어떤식으로 짜여졌어!&amp;#39; 라고 말을 해줬으면 좋았을 텐데 라고 생각을 하다가 보니 행동 중심 개발을 하면 좋겠다고 생각했다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;BDD는 행동을 기반하여 TDD를 수행하자는 공통의 이해인데, 이를 한 문장으로 하면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BDD는 애플리케이션이 어떻게 행동해야 하는지에 대한 공통된 이해를 구성하는 방법이다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;BDD의 행동&lt;/h3&gt;
&lt;p&gt;BDD의 행동 스펙을 다음과 같다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Narrative&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;모든 테스트 문장을 Narrative하게 되어야 한다. 즉, 코드보다 인간의 언어와 유사하게 구성되어야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BDD는 TDD를 수행하려는 어떤한 행동과 기능을 개발자가 더 이해하기 쉽게하는 것이 목적이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;모든 테스트 문장은 Given When Then으로 나눠서 작성할 수 있어야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Given/When/Then&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Given&lt;ul&gt;
&lt;li&gt;테스트를 위해 주어진 상태&lt;/li&gt;
&lt;li&gt;테스트 대상에게 주어진 조건&lt;/li&gt;
&lt;li&gt;테스트가 동작하기 위해 주어진 환경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;When&lt;ul&gt;
&lt;li&gt;테스트 대상에게 가해진 어떠한 상태&lt;/li&gt;
&lt;li&gt;테스트 대상에게 주어진 어떠한 조건&lt;/li&gt;
&lt;li&gt;테스트 대상의 상태를 변경시키기 위한 환경&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Then&lt;ul&gt;
&lt;li&gt;앞선 과정의 결과&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;*&lt;em&gt;즉, 어떤 상태에서 출발(given)하여 어떤 상태이 변화를 가했을 때(when) 기대하는 어떠한 상태가 되어야 한다.(then) *&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;예시를 하나 보자&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Calculator{
  public int plus(int a, int b){
    return a+b;
  }
}
public class CalculatorTest{
  Calculator calc = new Calculator();

  @Test
  void plus(){
    //given
    int a = 10;
    int b = 20;

    //when
    int result = calc.plus(a,b);

    //then
    assertEquals(result, a+b);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;별 차이가 없다. 당연하다. BDD 자체가 TDD에서 더 새로운 개념이 아니라 TDD를 더 잘, 더 멋지게, 더 협조적으로 사용하기 위한 방법이기 때문이다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;이름&lt;/th&gt;
&lt;th&gt;창시자&lt;/th&gt;
&lt;th&gt;Based&lt;/th&gt;
&lt;th&gt;핵심&lt;/th&gt;
&lt;th&gt;라이브러리&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;TDD&lt;/td&gt;
&lt;td&gt;Kent-Beck&lt;/td&gt;
&lt;td&gt;시나리오 기반&lt;/td&gt;
&lt;td&gt;테스트가 주도하는 개발&lt;/td&gt;
&lt;td&gt;JUnit5, Mockito&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BDD&lt;/td&gt;
&lt;td&gt;Dan North&lt;/td&gt;
&lt;td&gt;행동 기반&lt;/td&gt;
&lt;td&gt;자연어와 더 가깝게 TDD&lt;/td&gt;
&lt;td&gt;JUnit5, BDDMockito&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;BDDMockito를 이용한 BDD&lt;/h3&gt;
&lt;p&gt;BDD를 실현하기 위해서는 Given/When/Then구조를 잘 사용해야 한다. 앞선 방법 처럼 주석을 이용하여 구분하는 것도 BDD이지만 이보다 더 BDD스럽게 할 수 있는 것이 바로 BDDMockito라이브러리를 이용하는 것이다.&lt;/p&gt;
&lt;p&gt;순수 Mockito에서 BDD의 Given/When/Then을 사용하기 위해서 다음과 같이 when(obj) 메서드와 thenReturn() 메서드를 이용하고 verity() 구문을 이용해 검증한다.&lt;/p&gt;
&lt;p&gt;이런식으로 특정 상황에 대한 (when과 같이) 우리가 가짜로 결과를 만들어 주는 것을 Stubbing(스터빙) 이라고 한다. 즉 가짜로 수행할 객체를 넣어주는 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;when(phoneBookRepository.contains(momContactName)).thenReturn(false);

phoneBookService.register(momContactName,momPhoneNumber);

verity(phoneBookRepository).insert(momContactName, momPhoneNumber);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이게 일련의 BDD Stubbing과정인데, 뭔가 맞지 않는 부분이 있다. 우리가 알고있던 행동과정은 Given/When/Then인데 위에는 when()/thenReturn()/verity() 이다.&lt;/p&gt;
&lt;p&gt;개념적으로 given에 해당되는 Mockito의 when(phoneBookRepository.contains(momContactName)) 의 이름이 when이라 쉽게 헷갈리 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이러한 문제점을 BDDMockito의 given()메서드를 이용해 해결할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;BDDMockito를 이용하면 Mockito의 when()을 given() 이라는 메서드로 더 정확한 의미 전달을 할 수 잇따.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;given(someClass.method()).willReturn()&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;given() 메서드&lt;/h3&gt;
&lt;p&gt;Given()은 &lt;strong&gt;어떤 메서드가 실행되었을 때(!!!!!)의 테스트를 위한 상황을설정할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다음과 같은 상황을 메서드가 있다고 가정해보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public interface MemberRepository extends JpaRepository&amp;lt;Member,Long&amp;gt;{
  boolean existsByEmail(String email);
}
public class MemberService{
  @Autowired
  private MemberRepository memberRepository;

  public boolean isExistEmail(String email){
    return memberRepository.existsByEmail(email);// 존재하면 true, 존재하지 않으면 false 반환
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그럼 아래와 같은 테스트 코드를 구성할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public interface MemberRepository extends JpaRepository&amp;lt;Member,Long&amp;gt;{
  boolean existsByEmail(String email);
}
public class MemberService{
  @Autowired
  private MemberRepository memberRepository;

  public boolean isExistEmail(String email){
    return memberRepository.existsByEmail(email);// 존재하면 true, 존재하지 않으면 false 반환
  }
}
public class MemberServiceTest{
  @MockBean
  private MemberService memberService;
  private final MemberRepository memberRepository = mock(MemberRepository.class);

  @BeforeEach
  void stubbing(){
    given(memberRepository.exitsByEmail(any(String.class))).willReturn(false);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 중에서 아래의 코드를 분석해보자&lt;/p&gt;
&lt;p&gt;&lt;code&gt;given(memberRepository.exitsByEmail(any(String.class))).willReturn(false);&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;memberRepository.existsByEmail - Mocking할 메서드&lt;/li&gt;
&lt;li&gt;any(String.class) - 메서드의 파라미터&lt;/li&gt;
&lt;li&gt;.willReturn(false); - 해당 메서드가 반환하는 값&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 Stubbing 문장에서 3가지 의미를 담고있다.&lt;/p&gt;
&lt;h3&gt;Mocking 할 메서드&lt;/h3&gt;
&lt;p&gt;Unit Test에서 중요한 것은 테스트하려는 대상의 고립이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;테스트 대상을 고립한다는 뜻은, 테스트 대상에 연관된 다른 객체들은 관여하지 않도록 우리가 가짜 객체를 넣어줘야 한다는 이야기이다&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이렇게 테스트 대상을 고립하기 위해서 Mockito의 mock()를 이용하였고, 테스트 대상이 특정 결과를 수행하기 위해 연관된 객체의 연산을 주입해주면 된다.&lt;/p&gt;
&lt;p&gt;MemberService에는 현재 isExistsByEmail 메서드가 존재하고 해당 메서드 내부에 MemberRepository 인스턴스가 existsByEmail 연산을 수행하고 있으므로 우리는 MemberRepository의 existsByEmail을 가짜로 주입해주면 된다.&lt;/p&gt;
&lt;h3&gt;메서드의 파라미터&lt;/h3&gt;
&lt;p&gt;existsByEmail 이라는 메서든는 String으로 email을 받고 있다. 그럼 선택지가 주어진다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;모든 값을 받았을 때의 행동 정의하기&lt;/li&gt;
&lt;li&gt;특정 값을 받았을 때의 행동 정의하기&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;모든 값을 받았을 때의 행동 정의하기, any()&lt;/h3&gt;
&lt;p&gt;모든 객체가 들어왔을 때의 행동은 any( Object object)를 이용할 수 있다.&lt;/p&gt;
&lt;p&gt;여기서는 &lt;code&gt;any(String.class)&lt;/code&gt; 는 String인 모든 객체가 가능하다는 뜻이다. String 관련 any() 메서드는 여럿 존재한다. &lt;/p&gt;
&lt;p&gt;결국 &lt;code&gt;any(Object object)&lt;/code&gt; 의 의미는 Object 타입의 모든 객체라는 뜻이다. any() 내부적으로 Type을 읽고 판단하도록 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;anyString() : 어떤한 문자열&lt;/li&gt;
&lt;li&gt;anyLong() : 어떠한 Long&lt;/li&gt;
&lt;li&gt;anyBoolean() : 어떠한 Boolean값&lt;/li&gt;
&lt;li&gt;any() : 모든 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;특정 값을 받았을 때의 행동 정의하기 , eq()&lt;/h3&gt;
&lt;p&gt;만약 특정한 String 값이 들어와야 한다면 any()로는 판단할 수 없다. 그럴 때 사용하는게 바로 eq() 메서드이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;eq() : 동일한 값 혹은 객체의 행동&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;해당 메서드를 수행했을 때 반환하는 값&lt;/h3&gt;
&lt;p&gt;위와 같이 다양한 방법으로 행동을 정의했다. 그럼 이제 행동에 대한 적절한 반환을 해줘야 한다.&lt;/p&gt;
&lt;p&gt;행동을 반환할 때는 크게 3가지 방법이 존재한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;willReturn()&lt;/li&gt;
&lt;li&gt;will()&lt;/li&gt;
&lt;li&gt;willThrow()&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;willReturn()&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class MemberServiceTest{
  @BeforeEach
  void stubbing(){
    given(memberRepository.existsByEmail(any(String.class))).willReturn(false);
  }

  @Test
  void exists_valid(){
    boolean isExist = memberService.isExistEmail(&amp;quot;valid@gmail.com&amp;quot;);

    assertTrue(isExist);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;will() + invocation&lt;/h3&gt;
&lt;p&gt;will()은 willReturn과 조금 다르다&lt;/p&gt;
&lt;p&gt;will()에서는 invocation을 통해서 &lt;strong&gt;새로운 객체를 반환&lt;/strong&gt;하거나 아예 &lt;strong&gt;새로운 행동을 반환&lt;/strong&gt;할 수 있다.&lt;/p&gt;
&lt;p&gt;invoke : 호출하다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class MemberServiceTest{
  @BeforeEach
  void stubbing(){
    given(memberRepository.findById(anyLong))
      .will(invocation -&amp;gt; {
        Member member = invocation.getArgument(0);
        return MemberData.builder()
                                          .email(&amp;quot;test123@gmail.com&amp;quot;)
                                          .isFound(true)
                                          .build();
      });
  }
  @Test
  void exists_valid(){
    MemberData memberData = memberService.findMember(1L);

    assertNotNull(memberData);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;willThrow()&lt;/h3&gt;
&lt;p&gt;예외를 던지는 함수이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class MemberServiceTest{
  @BeforeEach
  void stubbing(){
    given(memberRepository.existsByEmail(eq(&amp;quot;hello@gmail.com&amp;quot;))).willThrow(MemberDuplicationException(&amp;quot;exists&amp;quot;))
  }
  @Test
  void exists_valid(){
    MemberDuplicationException exception = assertThrow(
        MemberDuplicationException.class, () -&amp;gt; memberService.isExistEmail(&amp;quot;valid@gmail.com&amp;quot;));

    assertEquals(exception.getMessage(), &amp;quot;exists&amp;quot;);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;그럼 몇가지 예제 코드를 보면서 테스트 코드를 읽어보자!!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@ExtendWith(MockitoExtension.class)
class TodoServiceTest {

    @Mock
    private TodoRepository todoRepository;

    @InjectMocks    // 위 todoRepository Mock을 주입받아서 사용할 todoService 정의
    private TodoService todoService;

    @Test
    void add() {
        when(this.todoRepository.save(any(TodoEntity.class)))
                .then(AdditionalAnswers.returnsFirstArg()); //TodoRepository가 save()메소드를 호출해서 TodoEntity값을 받으면 받은 엔티티 값을 반환하도록 설정

        TodoRequest expected = new TodoRequest();
        expected.setTitle(&amp;quot;Test Title&amp;quot;);

        TodoEntity actual = this.todoService.add(expected);

        assertEquals(expected.getTitle(), actual.getTitle());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;우리가 해석해야 할 문장은 &lt;code&gt;when(this.todoRepository.save(any(TodoEntity.class))).then(AdditionalAnswers.returnsFirstArg());&lt;/code&gt; 이다. &lt;/p&gt;
&lt;p&gt;일단 when()은 Mockito에 정의된 함수로 BDD관련 함수가 아니다 하지만 의미는 given행동과 똑같으니 해석할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;when(this.todoRepository.save()) : todoRepository에 정의된 save() 메서드가 호출되는 상태라는 뜻이다.&lt;/li&gt;
&lt;li&gt;save(any(TodoEntity.class)) : save() 메서드가 TodoEntity타입의 파라미터로 불린다는 뜻이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;결국 둘의 의미를 합해보면 &lt;strong&gt;todoRepository에 정의된 save()메서드가 TodoEntity 타입으로 한 파라미터를 가지채로 호출되는 상태&lt;/strong&gt; 라는 뜻이다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.then()&lt;/code&gt;은 OngoingStubbing 인터페이스에 정의된 함수이다. 결국 then의 의미로 해석하면 되는데 앞선 과정의 결과에 관련된 함수라고 생각하면 된다.&lt;/p&gt;
&lt;p&gt;이제 &lt;code&gt;AdditonalAnswers.returnFirstArg()&lt;/code&gt;의 의미가 궁금한데 command+ B(Mac 기준) 를 눌러서 설명을 보자&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class AdditionalAnswers {
    /**
     * Returns the first parameter of an invocation.
     *
     * &amp;lt;p&amp;gt;
     *     This additional answer could be used at stub time using the
     *     &amp;lt;code&amp;gt;then|do|will{@link org.mockito.stubbing.Answer}&amp;lt;/code&amp;gt; methods. For example :
     *
     * &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;java&amp;quot;&amp;gt;
     * given(carKeyFob.authenticate(carKey)).will(returnsFirstArg());
     * doAnswer(returnsFirstArg()).when(carKeyFob).authenticate(carKey);
     * &amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
     * &amp;lt;/p&amp;gt;
     *
     * &amp;lt;p&amp;gt;
     * This methods works with varargs as well, mockito will expand the vararg to return the argument
     * at the given position. Suppose the following signature :
     *
     * &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;java&amp;quot;&amp;gt;
     * interface Person {
     *     Dream remember(Dream... dreams);
     * }
     *
     * // returns dream1
     * given(person.remember(dream1, dream2, dream3, dream4)).will(returnsFirstArg());
     * &amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
     *
     * Mockito will return the vararg array if the first argument is a vararg in the method
     * and if the return type has the same type as the vararg array.
     *
     * &amp;lt;pre class=&amp;quot;code&amp;quot;&amp;gt;&amp;lt;code class=&amp;quot;java&amp;quot;&amp;gt;
     * interface Person {
     *     Dream[] remember(Dream... otherDreams);
     * }
     *
     * // returns otherDreams (happens to be a 4 elements array)
     * given(person.remember(dream1, dream2, dream3, dream4)).will(returnsFirstArg());
     * &amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
     * &amp;lt;/p&amp;gt;
     *
     * @param &amp;lt;T&amp;gt; Return type of the invocation.
     * @return Answer that will return the first argument of the invocation.
     *
     * @since 1.9.5
     */
    public static &amp;lt;T&amp;gt; Answer&amp;lt;T&amp;gt; returnsFirstArg() {
        return (Answer&amp;lt;T&amp;gt;) new ReturnsArgumentAt(0);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;해석을 해보자면&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;invocation의 첫번째 파라미터를 리턴해준다.&lt;/p&gt;
&lt;p&gt;만약 &lt;code&gt;given(person.remember(dream1,dream2,dream3)).will(returnFirstArg())&lt;/code&gt;는 dream1을 리턴해 준다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;우리 코드에서는 save()를 호출할때 넣었던 파라미터를 리턴해준다는 뜻이다.&lt;/p&gt;
&lt;p&gt;결국 총 해석은 &lt;strong&gt;todoRepository에 정의된 save()메서드가 TodoEntity 타입으로 한 파라미터를 가지채로 호출되면 넣었던 파라미터를 리턴해준다&lt;/strong&gt;를 stubbing한것이다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;참조 : &lt;a href=&quot;https://wonit.tistory/com/493&quot;&gt;JUnit5 BDDMockito로 알아보는 TDD와 BDD의 차이 및 BDD 실습&lt;/a&gt; &lt;/p&gt;</description>
      <category>Spring 공부</category>
      <category>any()</category>
      <category>bdd</category>
      <category>Java</category>
      <category>springboot</category>
      <category>TDD</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/102</guid>
      <comments>https://go-coding.tistory.com/102#entry102comment</comments>
      <pubDate>Mon, 24 Jan 2022 16:47:50 +0900</pubDate>
    </item>
    <item>
      <title>Java의 직렬화(Serialize)란?</title>
      <link>https://go-coding.tistory.com/101</link>
      <description>&lt;h1&gt;Java의 직렬화(Serialize)란?&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Java를 공부하고 Spring을 쓰다보면 계속해서 Serialize를 상속받은 클래스들을 볼 수 있었다. 도대체 직렬화란 무엇일까? 공부를 해보자&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;직렬화(Serialize)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 byte 형태로 데이터를 변환하는 기술.&lt;/li&gt;
&lt;li&gt;JVM(Jaava Virtual Machine 이하 JVM)의 메모리에 상주(힙 또는 스택)되어 있는 객체 데이터를 바이트 형태로 변환하는 기술&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;역직렬화(Deserialize)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;byte로 변환된 Data를 원래대로 Object나 Data로 변환하는 기술을 역직렬화(Deserialize)라고 부릅니다.&lt;/li&gt;
&lt;li&gt;직렬화된 바이트 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;직렬화 조건&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java.io.Serializable&lt;/code&gt;인터페이스를 상속받은 객체는 직렬화할 수 있는 기본조건입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public calss Member implements Serializable{
  private String name;
  private String email;
  private int age;

  public Member(String name, String email, int age){
    this.name = name;
    this.email = email;
    this.age = age = age;
  }
  @Override
  public String toString(){
    return String.format(&amp;quot;Member{name=&amp;#39;%s&amp;#39;, email=&amp;#39;%s&amp;#39;, age=&amp;#39;%s&amp;#39;}&amp;quot;, name,email)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;직렬화(Serialize) 방법&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java.io.ObjectOutputStream&lt;/code&gt; 를 사용하여 직렬화를 진행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static void main(String[] args){
    Member member = new Memer(&amp;quot;김배민&amp;quot;, &amp;quot;deliverykim@baemin.com&amp;quot;, 25);
  byte[] serializeMember;
  try(ByeArrayOutputStream baos= new ByteArrayOutputStream()){
    try(ObjectOutputStream oos= new ObjectOutputStream(baos)){
      oos.writeObject(member);
      // serializedMember -&amp;gt; 직렬화된 member 객체
      serializedMember = baos.toByteArray();
    }
  }
  //바이트 배열로 생성된 직렬화 데이터를 base64로 변환
  System.out.println(Base64.getEncoder().encodeToString(serializedMember));
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;역직렬화(Deserialize)조건&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;직렬화 대상이 된 객체의 클래스가 클래스 패스에 존재해야하며 import 되어 있어야 합니다.&lt;/li&gt;
&lt;li&gt;중요한 점은 직렬화와 역직렬화를 진행하는 시스템이 서로 다를 수 있다는 것을 반드시 고려해야합니다.&lt;/li&gt;
&lt;li&gt;자바 직렬화 대상 객체는 동일한 &lt;code&gt;serialVersionUID&lt;/code&gt; 를 가지고 있어야 합니다.&lt;ul&gt;
&lt;li&gt;&lt;code&gt;private static final long serialVersionUID= 1L;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;serialVersionUID&lt;/code&gt; 이 왜 필요한지 자세한 내용은 아래에 추가하였습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;역직렬화(Deserialize)방법&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java.io.ObjectInputStream&lt;/code&gt; 를 사용하여 역직렬화를 진행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static void main(String[] args){
  //직렬화 예제에서 생성된 base64 데이터
  String base64Member=&amp;quot;...생략&amp;quot;;
  byte[] serializedMember = Base64.getDecoder().decode(base64Member);
  try(ByteArrayInputStream bais = new ByteArrayInputStream(serializedMember)){
    try(ObjecInputStream ois = new ObjectInputStream(bais)){
      //역직렬화된 member객체를 읽어온다.
      Object objectMember = ois.readObject();
      Member member = (Member)objectMember;
      System.out.println(member);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;자바의 직렬화는 왜 사용하는가?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;복잡한 데이터 구조의 클래스의 객체라도 직렬화 기본 조건만 지키면 큰 작업 없이 바로 직렬화, 역직렬화가 가능합니다.&lt;/li&gt;
&lt;li&gt;데이터 타입이 자동으로 맞춰지기 때문에 관련 부분을 큰 신경을 쓰지 않아도 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;어디에 사용되는가?&lt;/h3&gt;
&lt;h4&gt;서블릿 세션(Servlet Session)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;세션을 서블릿 메모리 위에서 운용한다면 직렬화를 필요로 하지 않지만, 파일로 저장하거나 세션 클러스터링, DB를 저장하는 옵션 등을 선택하게 되면 세션 자체가 직렬화가 되어 저장되어 전달됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;캐시(Cache)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Ehcache, Redis, Memcached라이브러리 시스템을 많이 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;자바 RMI(Remote Method Invocation)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;원격 시스템 간의 메시지 교환을 위해서 사용하는 자바에서 지원하는 기술&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;자바의 직렬화 단점?&lt;/h3&gt;
&lt;h4&gt;역직렬화시 클래스 구조 변경 문제&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;기존 멤법 클래스를 직렬화합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Member implements Serializable{
  private String name;
  private String email;
  private int age;
  //생략
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;직렬화한 Data&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;rO0ABXNyABp3b293YWhhbi5ibG9nLmV4YW0xLk1lbWJlcgAAAAAAAAABAgAESQADYWdlSQAEYWdlMkwABWVtYWlsdAASTGphdmEvbGFuZy9TdHJpbmc7TAAEbmFtZXEAfgABeHAAAAAZAAAAAHQAFmRlbGl2ZXJ5a2ltQGJhZW1pbi5jb210AAnquYDrsLDrr7w=&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;멤버 클래스에서 속성을 추가합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Member implements Serializable{
  private String name;
  private String email;
  private int age;
  //phone 속성 추가
  private String phone;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;직렬화한 Data를 역직렬화하면 어떤 결과가 나올까요?&amp;gt;  결과는 &lt;code&gt;java.io.InvalidClassException&lt;/code&gt; 이 발생합니다.&lt;/li&gt;
&lt;li&gt;위에서 언급했던 것처럼 직렬화하는 시스템과 역직렬화하는 시스템이 다른 경우에 발생하는 문제입니다.&lt;/li&gt;
&lt;li&gt;각 시스템에서 사용하고 있는 모델의 버전 차이가 발생했을 경우에 생기는 문제입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;해결하기 위해서는&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;모델의 버젼간의 호환성을 유지하기 위해서는 &lt;code&gt;SUID(serialVersionUID)&lt;/code&gt; 를 정의해야 합니다.&lt;/li&gt;
&lt;li&gt;Default는 클래스의 기본 해쉬값을 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;또 다른 문제&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;String&lt;/code&gt;-&amp;gt;&lt;code&gt;StringBuilder&lt;/code&gt;,&lt;code&gt;int&lt;/code&gt;-&amp;gt;&lt;code&gt;long&lt;/code&gt;으로 변경해도 역직렬화에서 &lt;code&gt;Exception&lt;/code&gt;이 발생합니다.&lt;/li&gt;
&lt;li&gt;자바 직렬화는 상당히 타입의 엄격하다는 것을 알 수 있습니다.&lt;/li&gt;
&lt;li&gt;멤버 변수가 빠지게 된다면 Exception대신 null 값이 들어가는 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;직렬화 Data Size 문제&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;{&amp;quot;name&amp;quot;:&amp;quot;김배민&amp;quot;,&amp;quot;email&amp;quot;:&amp;quot;deliverykim@baemin.com&amp;quot;,&amp;quot;age&amp;quot;:25}
serializedMember (byte size = 146)
json (byte size = 62)&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;아주 간단한 객체의 내용도 2배이상의 차이를 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;일반적인 메모리기반의 Cache에서는 Data를 저장할 수 있는 용량의 한계가 있기 때문에 Json 형태와 같은 경량화된 형태로 직렬화하는 것도 좋은 방법입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;정리하며&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;외부 저장소로 저장되는 데이터는 짧은 만료시간의 데이터를 제외하고 자바 직렬화를 사용을 지양합니다.&lt;/li&gt;
&lt;li&gt;역질렬화시 반드시 예외가 생긴다는 것을 생각하고 개발합니다.&lt;/li&gt;
&lt;li&gt;자주 변경되는 비즈니스적인 데이터를 자바 직렬화를 사용하지 않습니다.&lt;/li&gt;
&lt;li&gt;긴 만료 시간을 가지는 데이터는 JSON 등 다른 포맷을 사용하여 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;a href=&quot;https://nesoy.github.io/articles/2018-04/Java-Serialize&quot;&gt;원글 출처&lt;/a&gt;&lt;/p&gt;</description>
      <category>JAVA</category>
      <category>Java</category>
      <category>serialize</category>
      <category>역직렬화</category>
      <category>직렬화</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/101</guid>
      <comments>https://go-coding.tistory.com/101#entry101comment</comments>
      <pubDate>Mon, 29 Nov 2021 15:48:25 +0900</pubDate>
    </item>
    <item>
      <title>@Size, @Length, @Column 차이점</title>
      <link>https://go-coding.tistory.com/100</link>
      <description>&lt;h1&gt;@Size, @Length, @Column 차이점&lt;/h1&gt;
&lt;p&gt;팀 프로젝트를 하면서 내가 JPA쪽 부분을 담당하게 되었다. Entity를 작성하던중 필드의 속성을 지정해줘야 했는데 필드의 사이즈를 무엇으로 주어야하나 고민을 했다. 구글링을 해보니 &lt;code&gt;@Size, @Length, @Column&lt;/code&gt; 3개중 하나로 필드의 사이즈를 지정해주었느데 도대체 무슨 차이인지 궁금했다.&lt;/p&gt;
&lt;p&gt; 이곳저곳을 찾아보던중 잘 설명해 놓은곳이 있어서, 이 사이트의 설명을 번역해서 올린다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.baeldung.com/jpa-size-length-column-differences&quot;&gt;https://www.baeldung.com/jpa-size-length-column-differences&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;1. Overview&lt;/h3&gt;
&lt;p&gt; &lt;a href=&quot;https://www.baeldung.com/javax-validation&quot;&gt;JSR-330&lt;/a&gt;의 @Size, &lt;a href=&quot;https://www.baeldung.com/hibernate-5-spring&quot;&gt;Hibernate&lt;/a&gt;의 @Length , 그리고 &lt;a href=&quot;https://www.baeldung.com/the-persistence-layer-with-spring-data-jpa&quot;&gt;JPA&lt;/a&gt;의 @Column에 대해서 살펴보겠습니다.&lt;/p&gt;
&lt;p&gt;언뜻 보기엔 비슷해 보이지만 서로 다른 기능을 하고있습니다. 알아봅시다.&lt;/p&gt;
&lt;h3&gt;2. Origins&lt;/h3&gt;
&lt;p&gt;간단히 말해서 이 annotation은 필드의 크기를 전달하기 위한 것입니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;@Size&lt;/code&gt;와&lt;code&gt;@Length&lt;/code&gt;는 비슷합니다. 둘다 필드의 크기에 검증하기위해 사용합니다. &lt;code&gt;@Size&lt;/code&gt;는 &lt;a href=&quot;https://docs.oracle.com/javaee/7/tutorial/bean-validation001.htm&quot;&gt;Java-standard annotation&lt;/a&gt;이고 &lt;code&gt;@Length&lt;/code&gt;는 &lt;a href=&quot;https://docs.jboss.org/ejb3/app-server/HibernateAnnotations/api/org/hibernate/validator/Length.html&quot;&gt;specific to Hibernate&lt;/a&gt;입니다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;code&gt;@Column&lt;/code&gt;은 DDL을 컨트롤 하기위해 사용되는 &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/persistence/Column.html&quot;&gt;JPA annotation&lt;/a&gt;입니다.&lt;/p&gt;
&lt;h3&gt;3. @Size&lt;/h3&gt;
&lt;p&gt;  검증을 위해서, @Size 검증 어노테이션을 사용합니다. @Size가 붙은 middleName속성을 사용하여 min, max 특성 사이의 값을 확인합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class User {

    // ...

    @Size(min = 3, max = 15)
    private String middleName;

    // ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; 가장 중요한 점은, &lt;strong&gt;@Size가 JPA와 Hinernate로부터 독립적인 bean을 만들어줍니다.&lt;/strong&gt; 결과적으로 @Size는 @Length보다 더 가볍습니다.&lt;/p&gt;
&lt;h3&gt;4. @Length&lt;/h3&gt;
&lt;p&gt; 그리고 말했듯이, @Length는 Hiberbate의 @Size어노테이션입니다. LastName 속성에 @Length를 사용해서 범위를 지정해 봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Entity
public class User {

    // ...

    @Length(min = 3, max = 15)
    private String lastName;

    // ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. @Column(length=value)&lt;/h3&gt;
&lt;p&gt; 하지만 @Column 은 많이 다릅니다.&lt;/p&gt;
&lt;p&gt; @Column은 실제 데이터베이스 열의 특정 특성을 나타내기 위해 사용됩니다. @Column의 length속성을 사용해서 열의 문자열의 길이를 특정해봅시다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Entity
public class User {

    @Column(length = 3)
    private String firstName;

    // ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; 열은 VARCHAR(3) 타입으로 생성되고 3보다 긴 문자열을 넣으려고 하면 SQL error가 나타납니다.&lt;/p&gt;
&lt;p&gt;@Column은 유효성 검사를 제공하지 않으므로 테이블 열 속성 지정에만 사용됩니다.&lt;/p&gt;
&lt;p&gt;물론, @Column 과 @Size를 같이 사용해 bean 검증을 통해 테이터베이스 열 특성을 지정할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Entity
public class User {

    // ... 

    @Column(length = 5)
    @Size(min = 3, max = 5)
    private String city;

    // ...

}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. Conclusion&lt;/h3&gt;
&lt;p&gt; 이 글에서 우리는 @Size과 @Length 그리고 @Column length 속성의 차이점에 대해서 배웠습니다. &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;역시 영어 문서는 영어 문서 그대로 읽는게 제일 좋은것 같다. 번역하려니 영 의미 전달이 힘들다.&lt;/p&gt;</description>
      <category>Spring 공부</category>
      <category>@Column</category>
      <category>@Length</category>
      <category>@Size</category>
      <category>JPA</category>
      <category>Spring</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/100</guid>
      <comments>https://go-coding.tistory.com/100#entry100comment</comments>
      <pubDate>Mon, 29 Nov 2021 10:52:15 +0900</pubDate>
    </item>
    <item>
      <title>개발자를 위한 글쓰기 가이드 후기</title>
      <link>https://go-coding.tistory.com/99</link>
      <description>&lt;h3&gt;개요&lt;/h3&gt;
&lt;p&gt; 회사에서 프로젝트나, 에러를 해결하면 관련된 문서를 작성해야할 일이 많았다. 하지만 항상 글을 쓸때마다 첫 문장을 작성하는 것 부터가 힘들었다. 글을 다 쓰고 난 뒤에도 만족스러운 글이 나오지 않았고, 내 글쓰기에 문제가 많다는 것을 느꼈다. &lt;/p&gt;
&lt;p&gt; 또한 회사에 지정된 양식이 없어서 답답함이 많았다. &lt;del&gt;내가 못 찾는 것일 수도..&lt;/del&gt; 마침 팀에내 한달에 한번 원하는 책을 읽고 독후감을 발표하는 일정이 생겨서 이 책을 읽었다.&lt;/p&gt;
&lt;img src=&quot;https://user-images.githubusercontent.com/54675591/135705408-7aede6cc-9a47-46de-b4fd-2ab3235d6138.png&quot; alt=&quot;image&quot; style=&quot;zoom:30%;&quot; /&gt;

&lt;hr&gt;
&lt;h3&gt;그냥 읽어보면 좋은 책&lt;/h3&gt;
&lt;p&gt; 책을 다 읽어본 후 든 내 생각은 &amp;quot;책 제목이 잘못되었는데?&amp;quot; 였다. &amp;quot;글쓰기 가이드&amp;quot;가 아니라 &amp;quot;글쓰기 꿀팁&amp;quot;이라고 바꿔야 한다고 생각이 든다.&lt;/p&gt;
&lt;p&gt; 책 내용이 글을 쓸때 유의해야할 단어나 문장, 어조, 쉽게 틀릴 수 있는 포인트들에 대해서 알려준다. 충분한 예시를 들면서 설명해주기 때문에 이해하기 쉬웠다. 평소에 생각하지도 못했던 포인트를 짚어주면서 어떻게 글을 써야 좋은 글을 쓸 수 있는지도 알게되었다. 하지만 가이드는 아니라고 생각한다. &lt;/p&gt;
&lt;p&gt; 처음 기대했던 내용은 기술 가이드 문서 작성 방법이나, 보편적으로 지켜야할 양식을 알려주는 내용을 기대했다. 내가 기대했던 내용과 왜 달랐던 것일까? 생각해 보면 회사마다 양식은 천차만별이다. 한마디로 양식 자체는 내가 만들어 가야한다. 이 때문에 지은이도 전체적인 양식에 대한 설명을 해주는 것이 아니라, 글쓰기를 하면서 얻었던 꿀팁들을 설명해주고 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;결국 많이 써봐야 한다.&lt;/h3&gt;
&lt;p&gt; 글쓰기를 잘 하려면 결국에는 많이 작성해봐야 한다. 이 책을 한번 읽고 바로 완벽한 문서를 만드는 것은 불가능하다. 여러가지 양식을 시도해 보면서 글을 써봐야 실력이 오를 것이다. 이 책의 내용은 글을 쓰면서 표현이 애매할 때 한번씩 펼쳐보면서 참고하면 좋을 것 같다. 이 책에 있는 내용들이 100% 맞는 것은 아니다. 왜냐하면 디테일한 양식은 결국 회사의 구성원들의 의견을 종합해서 정하기 때문이다. 이 책은 글쓰기 기본기로 생각하고 한번쯤은 읽어보는 것을 추천한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;어떤 사람이 읽으면 좋을까?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;IT기업에 종사하는 모든 사람들&lt;/li&gt;
&lt;li&gt;첫 문장을 시작하기 어려워하는 개발자&lt;/li&gt;
&lt;li&gt;본인의 글이 마음에 들지않는 개발자&lt;/li&gt;
&lt;li&gt;영업팀(IT 용어를 정리해서 쓰는 방법을 알 수 있을 것이다.)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>책 후기</category>
      <category>IT</category>
      <category>개발자를 위한 글쓰기 가이드</category>
      <category>글쓰기</category>
      <category>테크니컬라이팅</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/99</guid>
      <comments>https://go-coding.tistory.com/99#entry99comment</comments>
      <pubDate>Sat, 2 Oct 2021 15:13:59 +0900</pubDate>
    </item>
    <item>
      <title>ELK 란</title>
      <link>https://go-coding.tistory.com/98</link>
      <description>&lt;p&gt; ElasticSearch관련 연구과제를 서치하던 도중 ELK의 개념이 잘 정리되어 있는 블로그가 있어서 정리해본다.&lt;/p&gt;
&lt;h1&gt;ELK 란?&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/54675591/134859093-b6d69535-d297-4293-9aee-0eb3cb90118a.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt; ELK는 위 그림과 같이, 분석 및 저장 기능을 담당하는 ElasticSearch, 수집 기능을 하는 Logstash, 이를 시각화하는 도구인 Kibana의 앞글자만 딴 단어이다. ELK는 접근성과 용이성이 좋아 최근 가장 핫한 Log 및 데이터 분석 도구이다.&lt;/p&gt;
&lt;h3&gt;1. ElasticSearch&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ElasticSearch는 Lucene 기반으로 개발한 분산 검색엔지으로, Logstash를 통해 수신된 데이터를 저장소에 저장하는 역할을 담당한다.&lt;/li&gt;
&lt;li&gt;데이터를 중심부에 저장하여 예상되는 항목을 검색하고 예상치 못한 항목을 밝혀낼 수 있다.&lt;/li&gt;
&lt;li&gt;정형, 비정형, 위치정보, 메트릭 등 원하는 방법으로 다양한 유형의 검색을 수행하고 결합할 수 있다.&lt;/li&gt;
&lt;li&gt;엘라스틱서치(Elasticsearch)는 기본적으로 검색 엔진이다. 전통적인 관계형 데이터베이스의 잣대로 보면 아래와 같은 장점과 단점이 나타난다. 검색 엔진의 특성을 정확하게 이해하고 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Logstash&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;오픈소스 서버측 데이터 처리 파이프라인으로, 다양한 소스에서 동시에 데이터를 수집하고 변환하여 stash 보관소로 보낸다.&lt;/li&gt;
&lt;li&gt;수집할 로그를 선정해서 지정된 대상 서버(ElasticSearch)에 인덱싱하여 전송하는 소프트웨어이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. Kibana&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;데이터를 시각적으로 탐색하고 실시간으로 분석 할 수 있다.&lt;/li&gt;
&lt;li&gt;시각화를 담당하는 HTML + Javascript 엔진이라고 보면 된다.&lt;/li&gt;
&lt;li&gt;키바나(Kibana)의 경우 대체 가능한 소프트웨어로 그라파나(Grafana)가 있다. 데이터베이스 관리자가 주로 사용할 법한 기능을 일부 제외하고 UI측면을 강화한 특징이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;ELK Stack&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/54675591/134859956-efbd99e0-947c-40ac-a141-a1fc8db01929.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;ELK 솔루션에서 Beats가 추가되면서 ELK Stack이라고 불린다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Beats&lt;/p&gt;
&lt;p&gt;서버에 에이전트로 설치하여 다양한 유형의 데이터를 ElasticSearch 또는 Logstash에 전송하는 오픈 소스 데이터 발송자다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;장단점&lt;/h1&gt;
&lt;h3&gt;장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;강력한 유연성과 호환성&lt;/p&gt;
&lt;p&gt;Elasticsearch, Logstash, Kibana는 각각 데이터의 검색, 수집, 시각화를 담당한다. 용도별로 분리하여 발전하는 솔루션이기에 구조적 안정성은 물론 다른 시스템과도 유연한 호환성을 가진다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;자유 스키마&lt;/p&gt;
&lt;p&gt;JSON 방식의 Key-Value 형식의 데이터를 사용하므로 형식에 자유롭다. 인덱스의 Union, Join이 경우에 따라 와일드 카드 표기로 끝낼 수 있다.(여기서 인덱스는 Table에 해당한다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;확장(Scale-out) 가능 데이터베이스&lt;/p&gt;
&lt;p&gt;처음부터 확장을 고려하여 만들어졌기 때문에 여러대의 서버를 엮어서 성능 향상을 기대할 수 있는 클러스터 방식을 구성할 때 관계형 데이터베이스보다 상대적으로 관련 정보에 쉽게 접근이 가능하다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;데이터 처리 절차를 &amp;#39;레거시 코딩&amp;#39;보단 개별 설정으로 가능&lt;/p&gt;
&lt;p&gt;데이터 처리 절차를 프로그래밍 언어를 이용한 코딩으로 명시한다면, 향후 유지보수가 용이하지 않고 불필요한 종속성이 높아지는 문제가 있지만  Logstash를 포함하는 ELK Stack 구성 시 유연성 확보가 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;사전에 준비된 시각화 도구와 부가기능&lt;/p&gt;
&lt;p&gt;데이터를 시각화하여 보여주기 위한 UI를 내볼낼 프로그램을 따로 작성하지 않아도, 이미 사전에 준비된 시각화 도국를 가진 Kibaba가 마우스 조작으로 거진 다 알아서 해주낟. 그 외 데이터베이스 관리자를 위한 부가기능이 포함되어 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;실시간 데이터 처리&lt;/p&gt;
&lt;p&gt;Mesaage Queue(이하 MQ)와 결합하면 강력한 실시간 데이터 수집 및 처리 시스템이 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;엘라스틱서치의 버전 별 벌크(Bulk) 방법 차이를 완화&lt;/p&gt;
&lt;p&gt;엘라스틱서치의 벌크(Bulk)는 관계형 데이터베이스에서 삽입(Insert)에 해당한다. 프로그래밍으로 직접 구현할 경우 엘라스틱서치 버전 별 벌크 방법의 차이가 크기 때문에, 인터넷에 떠돌아다니는 튜토리얼만 보고 진행하기에는 오래된 자료도 많아 무리가 따를 수 있다. 로그스태시로 엘라스틱서치를 다룬다면 이런 걱정은 하지 않아도 된다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;초기 데이터 구성 및 이관 문제&lt;/p&gt;
&lt;p&gt;빠른 데이터 처리를 장점으로 표방하고 있으나, 초기 데이터 구성이 기존에 보유한 데이터를 엘라스틱서치로 이관하는 것에서 시작하는 경우가 많음에도 공식적으로 공개된 튜토리얼은 심각한 성능 저하를 일으킬 수 있는 내용이 주를 이루고 있다. 문제는 대부분 실시간 데이터를 처리하는데 적합한 방식을 대용량 데이터 이관에도 유용하다고 소개하는 점에서 비롯된다. 가령, MQ를 이용하면 실시간 데이터 처리를 분명 안정성과 속도면에서 강점이 있으나, 100GB 이상의 관계형 데이터베이스의 데이터 이관 절차에 그대로 적용하면 상당한 성능 저하의 우려가 있다. JDBC를 이용한 방법도 공식 튜토리얼이나 지나친 메모리 누수 문제로 인해 데이터가 몇 기가 정도면 되어도 사실상 데이터 이관 용도로는 사용이 불가능하다. 하지만 해결책은 있다. 데이터 이관이 목적인 경우, 데이터베이스 테이블 내용 전체를 파일로 저장한 다음 파일비트(Filebeat)를 쓰는게 훨씬 빠를 수 있다. 기존 데이터의 용량이 너무 커서 어쩔 수 없이 네트워크를 이용해도 로그스태시의 접점에서 속도 저하 및 메모리 누수를 최소화할 수 있도록, 목표한 파일이나 데이터베이스 테이블의 행을 하나 이상 씩 일정한 수로 읽으면서 TCP 또는 UDP 등 소켓을 기반으로 데이터를 전송하는 프로그램을 직접 구현하면 해결이 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;커널 변수의 불필요한 사용&lt;/p&gt;
&lt;p&gt;ELK 스택을 구성하는 솔루션 중 로그스태시는 리눅스 운영체제 커널의 cgroups에 해당하는 변수를 참조하는 내용이 포함되어 있는데, 이것은 운영체제에서 정한 임계값을 파악하기 위함이다. 하지만, 커널 컴파일 설정에 따라 cgroups 내의 변수의 일부는 존재하지 않을 수 있으에도, 데이터 수집이라는 목적에 변수가 존재하지 않는 이유로 프로그램이 중단되는 문제점이 있다. 주로 운영체제의 커널 크기를 최소화하려는 시도가 반영되었거나, 커널 공유 방식의 가상화를 진행하는 환경에서 문제가 발생하여 정상 설치 리눅스 운영체제의 경우 문제가 발생하지 않는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;초창기부터 현재까지도 원활하지 못한 시간대(Timezone)처리&lt;/p&gt;
&lt;p&gt;3개의 스택(로그스태시-&amp;gt;엘라스틱서치-&amp;gt;키바나)로 데이터가 넘어가는 과정에서 시간대 일관성이 깨지는 문제가 있다. &amp;#39;Asia/Seoul&amp;#39;같이 명시를 하여도 마치 2중 환전을 연상케하는 시간대 변환 오류를 쉽게 범한다. 하지만 3개 솔루션 모두 기본 시간대가 UTC라는 점을 인지하고, 시간대 설정이 가능한 경우 일관성을 유지하려는 노력으로 극복이 가능하다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;a href=&quot;https://velog.io/@courage331/ELK&quot;&gt;출처&lt;/a&gt;&lt;/p&gt;</description>
      <category>CS지식</category>
      <category>Elasticsearch</category>
      <category>Elk</category>
      <category>Kibana</category>
      <category>Logstash</category>
      <author>고코딩</author>
      <guid isPermaLink="true">https://go-coding.tistory.com/98</guid>
      <comments>https://go-coding.tistory.com/98#entry98comment</comments>
      <pubDate>Mon, 27 Sep 2021 16:49:29 +0900</pubDate>
    </item>
  </channel>
</rss>