2018년 12월 13일 목요일

Git 명령어 통해서 저장소 옮기기


Git 명령어 통해서 저장소 옮기기

#기존 저장소명 변경
git remote rename origin origin_backup
#변경할 저장소 추가
git remote add origin https://github.com/mandiwise/awesome-new-repo.git
#저장소로 파일 업로드
git push origin master
#기존 저장소 삭제
git remote rm origin_backup

Share:

2018년 10월 21일 일요일

OKKY Conference 2018 The Real TDD 세미나 후기


OKKY Conference 2018 The Real TDD - TDD 제대로 알기 -

#1 테스트하기 쉬운 코드로 개발하기

정진욱 / PUBLYTO CPO
  • 테스트하기 쉬운 코드란?
    • 항상 같은 결과 반환
    • 외부상태를 변경하지 않음
  • 테스트하기 쉬운 코드로 개발하기
    • 방법1: 테스트하기 쉬운코드와 어려운 코드 분리
      • 요청한 좌석 수가 확보 가능한지 판단하기 위한 코드를 테스트 하기 위해서는 DB에 테스트 데이터를 설정해야한다.
    • 방법2 : 두 분류의 코드는 어디서 만나야 하나?
      • 두 분류의 코드는 최대한 가장 자리에 위치(에외 : 로깅, 퍼사드)
      • 테스트 하는 메소드에서 테스트 하기 어려운 코드가 제일 안쪽에 있으면 어려움이 전파되서 전체적으로 테스트하기 어려운 메소드가 된다.
    • 방법3 : 두 부류 코드가 만나는 가장자리는 어떻게 테스트 해야 하나?
      • 가장 자리를 테스트하는 방법을 익히자
      • 수동테스트
        • postman 사용
        • DB의 경우 query
      • 자동테스트
        • 작성된 프로덕션 코드의 사용 강제
        • 현 상태의 코드로는 할 수 없다
        • 실제 클래스 대신 목 사용을 위해 이음새가 있어야 한다.
        • 목 사용
          • 작성된 코드 사용을 강제할 수 있다.
          • 목 사용이 큰 장점으로 보이지만, 생각해 볼 점이 있다.
            • 행위 검증
              • 행위가 호출되었는가
              • Mockist
              • 불필요한 추상화 유발 가능성
            • 상태검증
              • 결과 값이 무엇인가
              • Classcist
              • 불필요한 추상화 필요 없음
        • 문제점
          • 목을 남발할 가능성이 크다.
            • 대부분 목 사용 예제는 간단하다. 그래서 장점이 크게 보인다.
            • 실제 프로젝트에 적용하면 한꺼번에 많은 수의 목을 다루면서 곤란을 겪는다.
          • 적당 수의 목 사용에 대한 답을 찾기 어렵다.
          • 상태 검증으로 돌아가보자
        • 상태 검증 - 문제 극복 방안
          • TDD를 통한 사전이 아니라 사후 테스트를 하자
          • 두 부류의 코드가 맞물려 잘 돌아가는 로직이다
          • 난해한 코드가 아니다
          • 구현된 코드를 사용하지 않고 굳이 어려운 길을 택할 이유가 없다
          • 완벽을 추구하면서 목을 사용하는 비용을 들일 필요가 있는가?
  • 끝으로
    • 요즘 저는
      • 두 분류 코드를 분리해서 각각 테스트하고,
      • 가장자리에서 맞물려 돌아가는 코드는 주로 수동테스트
    • 두 부류 코드 섞어 놓고 테스트가 어렵다고 포기하지 마세요
    • 위 내용과 관련된 제 블로그 참조
  • 테스트를 만족하는 만큼만 코드를 작성하라
  • TDD는 점진적인 접근방법을 사용하고 있다.
  • 테스트하기 어렵다는 거는 테스트 할때 값이 필요하다는 뜻이다.
  • Q & A
    • private 코드 테스트 하는 방법?
      • 같은 기능을 테스트해도 적은 메소드를 건드리는 메소드가 좋다
    • 레거시 코드 테스트 하는 방법?
      • 최대한 레거시 코드와 결합도를 낮추자
      • 목업을 할수 있는 seam을 만든다
      • 최대한 새로 생성된 코드에 최대한 테스트를 가한다
      • 기존 레거시 코드에 대한 값을 정의해 놓고 테스트

#2 의식적인 연습으로 TDD, 리팩토링 연습하기

박재성 / SW 교육 전문가 전 NEXT 교수
TDD와 리팩토링을 왜 해야 하는지 알고 있다. 는 가정 하에 진행한다.
TDD와 리팩토링을 비슷한 비중으로 다룬다. 어쩌면 TDD < 리팩토링 일지도 모른다.
개발 현장을 떠난지 오래 되어서 틀린 부분이 있을 수도 있다
  • 의식적인 연습으로 TDD, 리팩토링 연습 과정
  • TDD, 리팩토링을 잘 하려면??
    • 연습..연습..
    • 연습을 많이 한다고 잘할 수 있을까?
    • 6년한 후에 알게된 점
      • 테스트하기 쉬운 코드와 테스트하기 어려운 코드를 보는 눈
      • 테스트하기 어려운 코드를 테스트하기 쉬운 코드로 설계하는 감
  • 의식적인 연습의 7가지 원칙
    • 첫째, 효과적인 훈련 기법이 수립되어 있는 기술 연마
    • 둘째, 개인의 컴포트 존을 벗어난 지점에서 진행, 자신의 형재 능력을 살짝 넘어가는 작업을 지속적으로 시도
    • 셋째, 명확하고 구체적인 목표를 가지고 진행
    • 넷째, 신중하고 계획적이다. 즉, 개인이 온전히 집중하고 '의식적'으로 행동할 것을 요구
    • 다섯째, 피드백과 피드백에 따른 행동 변경을 수반
    • 여섯째, 효과적인 심적 표상을 만들어내는 한편으로 심적 표상에 의존
    • 일곱째, 기존에 습득한 기술의 특정 부분을 집중적으로 개선함으로써 발전시키고, 수정하는 과정을 수반
  • 연습하는 과정
    • 1단계 - 단위 테스트 연습
      • 내가 사용하는 API 사용법을 익히기 위한 학습 테스트에서 시작
        • JAVA API 테스트(split, subString, ArrayLisy ...)
      • 내가 구현하는 메소드중 Input과 Output이 명확한 클래스 메소드(보통Util 메소드)에 대한 단위 테스트 연습
      • 알고리즘을 학습한다면 알고리즘 구현에 대한 검증을 단위 테스트로 한다.
      • 지켜야 할 원칙
        • 회사 프로젝트에 연습하지 말고 장난감 프로젝트를 활용해 연습하자.
        • 실패하는 테스트 소스 -> 성공하는 테스트 소스 - > 리팩토링을 반복
        • 테스트 코드를 먼저 만들고 코드를 만든다.
        • 어려운 문제를 해결하는 것이 아니라 TDD 연습이 목적 난이도가 낮거나 자신에게 익숙한 문제로 시작하는 것을 추천
    • 2단계 - TDD연습
      • 회사 프로젝트 말고 장난감 프로젝트를 활용하자.
      • 의존관계를 가지지 않는 장난감 프로젝트를 활용하자.
      • TDD 사이클

        • 처음에는 1단계, 2단계만 하고 3단게 Refactoring은 안해도 된다.
      • 리팩토링 연습
        • 한 메서드에 오직 한 단계의 들여쓰기만 한다.(메서드로 분리)
        • else 예약어를 쓰지 않는다.
          • return을 중간에 하면 하단 코드를 안봐도 된다.
          • 가독성 증가
        • 메소드가 한 가지 일만 하도록 구현하기
        • 로컬 변수가 정말 필요한가?
        • compose method 패턴 적용
        • 모든 원시값과 문자열을 포장한다.
        • 일급 콜렉션을 쓴다.
        • 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.(힘들다)
      • 테스트 코드는 변경하지 말고 테스트 대상 코드를 개선하는 연습을 한다.
      • 읽기 좋게 코드를 변경한다.
      • 추상적인 피드백을 받으면 어느 부분을 작업 해야할지 알기 어렵다.
    • 3단계 - 연습을 과하게 한다
      • 연습을 할때는 극단적으로 해야 느끼는 점이 있다
      • 처음 읽는 사람에게 어느 코드가 더 읽기 좋을까? 리팩토링 전, 후
      • 한번에 모든 원칙을 지키면서 리팩토링하려고 연습하지 마라. 한번에 한 가지 명확하고 구체적인 목표를 가지고 연습하라.
      • 연습을 극단적인 방법으로 연습하는 것도 좋다. 예를 들어 한 메소드의 라인 수 제한을 15라인 -> 10라인으로 줄여가면서 연습하는 것도 좋은 방법이다.
      • 테스트 코드가 있으니 리팩토링 연습이 가능하다.
    • 4단계 - 장난감 프로젝트 난이도 높이기
      • 점진적으로 요구사항이 복잡한 프로그램을 구현한다. 앞에서 지켰던 기준을 지키면서 프로그래밍 연습을 한다.
      • TDD하기 좋은
        • 게임과 같이 요구사항이 명확한 프로그램으로 연습
        • 의존관계가 없이 연습
        • 약간은 복잡한 로직이 있는 프로그램
      • 연습하기 좋은 예
        • 로또
        • 사다리 타기
        • 볼링 게임 점수판
        • 체스 게임
        • 지뢰 찾기 게임
    • 5단계 - 의존관계 추가를 통한 난이도 높이기
      • 웹, 모바일 UI, 데이터베이스의 의존관계를 테스트
      • 필요한 역량
        • 테스트하기 쉬운 코드와 테스트하기 어려운 코드를 보는 눈
        • 테스트하기 어려운 코드를 테스트하기 쉬운 코드로 설계하는 감
      • 앞 단계 연습을 잘 소솨했다면 테스트하기 쉬운 코드와 어려운 코드를 분리하는 역량이 쌓였을 것이다.
      • 한 단계 더 나아간 연습을 하고 싶다면
        • 컴파일 에러를 최소화하면서 리팩토링하기
        • ATDD 기반으로 응용 애플리케이션 개발하기
        • 레거시 애플리케이션에 테스트 코드 추가해 리팩토링하기
      • 유지보수를 잘하는 SW개발자가 몸값이 더 올라갈 것이다

        • 소트웍스 앤솔러지
        • 클린코드
    • TDD, 리팩토링 연습을 위해 필요한 것은?
      • 조급한 대신 마음의 여유
      • 나만의 장난감 프로젝트
      • 같은 과제를 반복적으로 구현할 수 있는 인내력
    • 가장 필요한 것은 가보지 않는 길에 꾸준히 도전할 수 있는 용기
  • Q & A
    • 컴포트존을 극복하는 방법?
      • 삶에 여유와 에너지가 있어야 한다.
      • 회사 말고 집에서 혼자 연습을 하자.
    • 기존 테스트 코드가 있는데 요구사항이 추가, 변경 됐을때 기존 테스트 코드가 에러 났을때 대처
      • 내가 테스트 코드를 잘못 짠 경우가 많다.
      • 나중에 하지 말고 현재에 최대한 테스트 코드를 작성하자.

#3 코드 품질을 위한 테스트 주도 개발

삼성SDS 한성곤 Principal Engineer
  • TDD Cycle
    • Red -> Green -> Refactor 반복
    • 짧은 개발 주기의 반복에 의존하는 개발 프로세스
    • XP의 Test-First 개념을 기반으로, 단순한 설계를 중요시 하는 Pratice
  • TDD 장점
    • FROM Test
      • 동작하는 코드에 대한 자신감
      • 회귀테스트를 통한 자유로은 리팩토링
      • 코드에 대한 지식이 증가
      • 개발 생산성 향상
    • FROM Test-first
      • 과도한 설계를 피하고, 간결한 interface를 가짐
      • 불필요한 기능을 줄임
      • 실행 가능한 문서를 가짐
      • 코드 품질을 높임
      • 테스트 코드가 있으면 디버깅에 대한 시간이 줄어든다.
  • SonarCloud
    • 프로젝트의 코드 퀄리티를 분석
  • 사례
    • Microsoft 사례
      • TDD / Non TDD 팀
        • TDD가 하는 팀이 15% 더 많은 시간 소요
  • 불필요한 주석은 나쁘다. 버그성에 대한 내용을 주석에 담는 경우
  • 코드 퀄리티 매트릭스
    • 복잡도
    • 결합도
    • 응집도
  • MCC 측정 평가
    • 마이크로소프트는 McCabe 수치를 임계치 15, 10이상은 refactoring 교려 대상

#4 테알못 신입은 어떻게 테스트를 시작했을까?

이혜승 / 오마이호텔 Front-End Engineer
  • 방법에 대한 이야기
    • 테스트
      • 1-1 기존 코드에 테스트 추가하기
        • 이미 테스트 하기 쉽게 만들어져 있는 코드로 시작한다(순수함수, 외부 의존성 있는것은 말고)
        • 프로덕션 코드는 수정하지 않고 테스트 한다.
        • 테스트코드는 수정하지 않고 리팩토링을 진행한다.(리팩토링은 테스트가 깨지지 않는 범위 내에서만)
        • 테스트 하기 어렵게 만들어져 있는 코드에서 테스트 하기 쉬운 것만 분리한다.
          • 선택 기준
            • 중요도가 높은 비즈니스 로직이 포함된 부분
            • 버그가 발견된 부분 (과거X)
            • 결합이 낮고 논리는 복잡한 부분(외부 의존도가 낮거나 없고(독립적인 환경을 가지고 있고) 비즈니스 로직은 복잡한)
        • 테스트 가능한 코드를 찾아서 -> 분리하고 -> 테스트 코드를 추가하고 -> 리팩토링
        • 코드가 테스트 하기 어려운 이유(API 요청,응답 결과에 따라 다른 UI)는 테스트 가능한 부분만 분리한다.
          • 외부에 영향을 받지 않는 독립적인 함수로 만들어 준다.
          • 반환 값에 대한 스펙을 추가한다.
    • TDD
      • 1-2 새로운 코드는 TDD로 개발하기
        • 비즈니스 요구 사항 추가
    • 정리
      • 어떻게
        • 기존 코드에 테스트만 추가(+리팩토링)
        • 새롭게 추가 되는 요구 사항은 TDD
      • 무엇을
        • 이미 테스트 하기 쉬운 코드를 만들어서
  • 경험에 대한 이야기
    • 좋은점
      • 새로운 비즈니스가 추가 됐을때 코드를 수정 후 문제 점이 있을때 기존에 있는 오류를 테스트코드가 잡아준다.
      • 진짜로 느낀 것들
        • 스펙 문서 기능
        • 디자인 개선 효과
        • 학습 동기부여
          • 내가 얼마나 디자인을 못하는 지 알게 된다.
          • 안정성과 설계 관심을 가지게 된다.
        • 개발 생산성 향상
          • 버그를 추적하는 시간이 현저하게 줄어든다.
        • 프로젝트 생산성 향상
          • 비즈니스 로직의 허점을 사전(본격적으로 코딩을 시작하기 전에)에 발견하기도 한다.
        • 집중력 향상
          • TDD는 동시에 한 가지 이상의 일을 하지 않도록 통제해준다.
    • 실수
      • 테스트 대상 오류
        • 불필요한 테스트
          • 불필요하다의 기준 : 비즈니스와 관련된 버그를 낼 가능성이 낮거나 없고 테스트를 유지함으로써 얻는 이익 < 테스트 유지와 관리에 드는 비용 일 때, 테스트가 단언하고 있는 내용이 사용자에게 중요한 가치를 주는 것이 아닐때
        • 필요하지만 검증 방식이 잘못된 테스트
          • 특정 위치에 대한 테스트는 변경될 소지가 많다.
          • 검증식을 개선한다.
        • 검증력이 떨어지는 테스트
          • 하나 이상의 가능성을 열어 두고 있다.
        • 테스트 제목과 검증의 불일치
        • 테스트를 앞서가는 프로덕션 코드
          • 프로덕션 코드에서 미리 모든 요소를 만든다.
          • 테스트가 성공할 만큼의 최소한의 프로덕션 코드를 만든다의 원칙을 어긴다.
    • 고민
      • 테스트 관련 코드들의 관리
        • 테스트를 위해 만들어진 데이터(Fixture)
      • 이런방식으로 하다보니 함수가 곳곳에 퍼지게 된다
        • 왠지 모르겠지만 불안하다.
        • 계속 이렇게 함수로 분리하기만 하는 게 맞는 건가?
        • 오히려 전부 분리하는 게 가독성을 더 해치고
        • 추상화 수준이 낮다
          • 아주 구체적인 작은 단위의 함수들로만 분리되어 있고 그들이 조합되어 만들어지는 큰 수준의 기능은 설계되지 않았다. 그래서 추상화 수준이 낮아졌고 클라이언트에서 구체적인 내용을 알아야만 사용될 수 있다.
        • 높은 응집, 낮은 결합

#5 테스트를 돌보기 위한 매우 간단한 실천 방법들, 그리고 효과

양완수 / 쿠팡 Principal, Software Engineer

테스트 코드의 가독성

  • 질문
    • TDD를 실무에서 많이 하느냐? 아니다.
    • Unit Test를 작성을 하고 있느냐? 할 때도 있고 안 할 떄도 있다.
  • 발표 내용
    • TDD에 대해서 이야기하지 않습니다.
    • 테스트하기 좋은 설계에 대해서 이야기하지 않습니다.
    • 제어할 수 없는 것에 대해서 이야기하지 않습니다.
  • 테스트 코드는 프로젝트 코드가 사용되는 최초의 장소이며 고객이다.
  • 모든 역사는 테스트 코드부터 시작된다.
  • 추상이란?
    • 문맥 위에서 오직 관심 있는 것들에 대해서만 집중하여 명확하게 하는 것
  • 당신이 테스트를 거들떠 보려 하지 않는 이유
    • 귀찮다.
    • 고통스럽게 하는것
      • 숨겨진 본질
        • 낮은 추상화
        • 들쭉날쭉한 추상화
        • 끊어진 논리
        • 알 수 없는 의도
      • 욕심쟁이
        • 테스트가 실패하는 이유는 단 하나
        • 하나의 테스트는 오직 한 가지만 똑바로 검사해야 한다.
      • 인지능력의 과부화
        • 흩어진 코드와 데이터
        • 매직 넘버
      • 꺠지기 쉬운 것들
        • 높은 결합
        • 낮은 응집
  • 백문이 불여 일코
    • 좋은 설계를 가진 코드는 아니다.
    • 다양한 가느성을 가진 코드
    • 실제 팀 내 코드에서 발췌하여 발표에 맞게 수정
    • 불필요한 것에 집착하지 않습니다.(네이밍)
    • 테스트 코드를 위한 프로덕트 코드
    • url작성하기 Git
    • 코드 커밋 순서 설명
      • 무슨 테스트인지 알 수 없는 코드
      • setUp메소드를 통해 응집력이 다른 코드를 분리하고 추상화 레벨을 맞추려고 한다.
      • 객체를 새로 만들어 2가지 객체의 관계를 가깝게 만든다.
      • 테스트 대상을 명확하게 표현한다.(네임변경)
      • 한가지만 확실하게 테스트를 대칭성 있게 하자
      • 응답에는 성공과, 실패가 있다. 의존을 제거하고 불필요한 것을 격리 한다.
      • 테스트명을 처음부터 스트레스 받지 말고 안의 내용을 명확하게 만든 후 수정하자.
      • 하나의 것만 테스트 해라.
  • 테스트 코드를 잘 작성하려고 노력하다보면 좋은 설계를 가진 코드(높은 응집도 낮은 결합도를 가진 코드)를 가지게 된다.
  • 테스트가 쉽도록 코드를 만들어야 한다.
  • 테스트는 과정, 수련, 스승, 거울, 제품이다. 억지로 먼저 하지마라. 건강하게 하라. 두려워 하지마라. 즐거워 해라.
  • 테스트는 우리를 가르쳐준다. 우리를 가이드하고 발전 시켜준다. 프로덕션 코드를 좋게 만들어 준다.

#6 당신들의 TDD가 실패하는 이유(Live Coding)

이규원 / 오마이호텔 CTO
  • TDD가 실패 하는 이유
    • 준비가 되지 않아서
    • 개인
      • 모든 것에 TDD를 하지 않는다.
      • 우리가 보호해야 하는 것
        • 도메인
      • 우리가 제어할 수 없는 것
        • 외부 세상
          • 실 세계
          • 인프라
          • 외부 서비스
          • 레거시
      • 설계
        • 낮은 결합
        • 높은 응집
        • 도메인 모델 보호
      • 설계를 테스트 하라
      • 문제점
        • 정보 숨김(Information Hiding)
          • 어려운 설계 결정과 변경될 가능성이 높은 설계 결정들을 다른 모듈로부터 숨기는 것
        • 구현을 테스트
          • 내부의 코드를 수정하면 테스트가 깨진다.
        • 설계 테스트
          • 내부의 코드를 수정해도 테스트가 깨지지 않는다.
      • 레거시와 함께 살기
        • 새로 작성한 코드를 위해 Adapter로 감싸고 테스트 한다.(System Under Test)

      • 프로세스
        • 점진
        • 반복
        • Fail-Fast
      • 반복 주기
        • 주로 실행만 한다.(코드만 작성)
        • 계획
        • 실행
        • 평가
      • 문화
        • 공유
          • 목표
          • 지식
      • 아키텍처
        • 낮은 결합
        • 높은 응집
        • 도메인 모델 보호
      • 도메인 모델과 플랫폼
        • 도메인 모델과 플랫폼이 강하게 연결되어 있지 말고 서로 분리 해야 한다.
      • 아키텍처 사례(크기가 큰 순서로 나열)
        • 비즈니스로직 - 서비스 -모듈 - 애플리케이션 플랫폼
      • MVVM 아키텍처 패턴
  • 비즈니스에 대한 목적을 정확하고 정리하고 그림을 그리고 얘기를 해야 서로 이해에 대한 오해가 없다
  • ATDD(Acceptance Test) + TDD(Unit Test)로도 테스트 할 수 있다.
  • 코드 설계
    • 작업에는 어떤 코드 변경이 필요한가?
  • 피드백

#7 패널 토의 Q & A

  • 왜 많은 방법론 중에서 TDD인가?
    • 코드에 대한 신중함이 생긴다.
    • 가장 빠른 피드백을 준다.
    • 피드백을 통해 직무 탈진을 하지 않게 된다.
  • 주위 사람들에게 TDD를 어떻게 설득할 수 있을까?
    • TDD를 하는 개발자는 많지 않다. 외롭다.
    • 어떻게 같이 TDD를?
      • 주니어 개발자고 영향력이 크지 않다면 전파하는데 집착하지 말자
      • 나의 만족을 위해서 하자
      • 코드에 대한 자신감이 생긴다.
      • 주위 사람이 리팩토링한 코드에 대해 물어보면 그떄 전파 하자
      • 나중에 영향력이 있을떄 전파 하자
        • 하지만 과연 성공할까?
        • 팀에 대한 문제점을 개인이 인식하게 만든다.
        • 팀원들이 페어링과 TDD를 하자는 환경을 만들어야 한다.
        • 팀원들이 주도적으로 하게 만들어야 한다.
        • 먼저 TDD를 하는게 아니라 필요한 부분에 먼저 작은 변화를 적용한다.
      • 초보일때 남들과 같이 TDD를 하기에 수월할 수도 있다.
    • TDD에서는 테스트의 순서가 중요 하다. 첫 테스트는 작지만 핵심인 부분을 하자
    • TDD를 도입할 때도 핵심을 찾아야 한다.
  • 코드 커버리지의 기준은 어떠한 지표로써 이해를 해야 좋을까?
    • 테스트 커버리지에 집착 하지 말자
    • 프로덕션 코드에 집중 하자
  • 신입의 TDD공부방법 및 도서
    • 페어프로그래밍
    • 테스트 주도 개발(도서)
    • 테스트 및 리팩토링 책을 많이 보자
    • 책만 보지 말고 꾸준히 수련 하
  • 도메인 모델과 스프링의 접점을 줄여야 하는 이유
    • 여러 플랫폼에 종속적이지 않기 위해서
    • 스프링으로 개발하지 말고 순수 코드로 개발 완료 후 스프링에 옮겨 본다.
Share:

2018년 10월 19일 금요일

2018년 9월 27일 목요일

mysql 계층형 메뉴 정렬 하는 쿼리


계층형 메뉴 정렬 하는 쿼리

CREATE DATABASE test default CHARACTER SET UTF8;

use test;

DROP table IF EXISTS menu;
CREATE TABLE menu (
  no int PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  parent int NULL,
  depth int NOT NULL,
  sequence int,
  reg_date DATETIME,
  modify_date DATETIME
)ENGINE=INNODB;
DESCRIBE  menu;

DROP TABLE menu;

INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date) VALUES("메뉴1" ,0 ,1, 1, now(), now());
INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴1-1" ,1 ,2, 1, now(), now());
INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴1-2" ,1 ,2, 2, now(), now());
INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴1-1-1" ,2 ,3, 1, now(), now());

INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴2" ,0 ,1, 2, now(), now());
INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴2-1" ,5 ,2, 1, now(), now());
INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴2-2" ,5 ,2, 2, now(), now());
INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴2-1-1" ,6 ,3, 1, now(), now());

INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴1-1-2" ,2 ,3, 2, now(), now());
INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴2-1-2" ,6 ,3, 2, now(), now());
INSERT INTO menu(name, parent, depth , sequence, reg_date, modify_date)  VALUES("메뉴2-1-3" ,6 ,3, 3, now(), now());
SELECT * FROM menu;

/* 계층형 mysql 쿼리 정렬 함수 */
DROP FUNCTION IF EXISTS hierarchy_connect_by_parent_eq_prior_no;
DELIMITER $$
CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_no(value INT) RETURNS INTEGER
NOT DETERMINISTIC
READS SQL DATA
  BEGIN
    DECLARE _parent INT;
    DECLARE _rank INT;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;

    SET _parent = @id;
    SET _rank = 0;

    IF @id IS NULL THEN
      RETURN NULL;
    END IF;

    LOOP
      SET @innerrank = 0;
      SELECT p.no
          INTO   @id
      FROM   (
             SELECT   no, @innerrank := @innerrank+1 AS ran
             FROM     menu
             WHERE    COALESCE(parent, 0) = _parent
             ORDER BY sequence
             ) p
      WHERE   p.ran > _rank LIMIT 0, 1;
      IF @id IS NOT NULL OR _parent = @start_with THEN
        SET @level = @level + 1;
        RETURN @id;
      END IF;
      SET @level := @level - 1;
      SET @innerrank = 0;
      SELECT COALESCE(p.parent, 0), p.ran
          INTO   _parent, _rank
      FROM   (
             SELECT no, parent, @innerrank := @innerrank+1 AS ran
             FROM    menu
             WHERE   COALESCE(parent, 0) = (
                                           SELECT COALESCE(parent, 0) FROM menu WHERE no = _parent
                                           )
             ORDER BY sequence
             ) p
      WHERE p.no = _parent;
    END LOOP;
  END;
$$
DELIMITER ;

/* 계층형 메뉴 정렬*/
SELECT  CONCAT(REPEAT('    ', depth  - 1), CAST(ou.no AS CHAR)) AS no, parent, depth, CONCAT(REPEAT('    ', depth  - 1), CAST(ou.name AS CHAR)) AS name, sequence, level FROM (
                 SELECT mn.name , mn.no, depth , parent, sequence, level FROM (
                                                               SELECT hierarchy_connect_by_parent_eq_prior_no(no) AS no,
                                                                      @level AS level
                                                               FROM (
                                                                    SELECT @start_with := 0, @id := @start_with, @level := 0
                                                                    ) vars, menu
                                                               WHERE @id IS NOT NULL
                                                               ) m
                                                                 JOIN menu mn ON mn.no = m.no
                 ) ou;

/* 계층형 mysql 쿼리 정렬 함수 - 특정 번호 */
DROP FUNCTION IF EXISTS hierarchy_connect_by_parent_eq_prior_id;
DELIMITER $$
CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INTEGER
NOT DETERMINISTIC
READS SQL DATA
  BEGIN
    DECLARE _no INT;
    DECLARE _parent INT;
    DECLARE _next INT;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET @no = NULL;

    SET _parent = @no;
    SET _no = -1;

    IF @no IS NULL THEN
      RETURN NULL;
    END IF;

    LOOP
      SELECT  MIN(no)
          INTO    @no
      FROM    menu
      WHERE   parent = _parent
        AND no > _no;
      IF @no IS NOT NULL OR _parent = @start_with THEN
        SET @depth = @depth + 1;
        RETURN @no;
      END IF;
      SET @depth := @depth - 1;
      SELECT  no, parent
          INTO    _no, _parent
      FROM    menu
      WHERE   no = _parent;
    END LOOP;
  END;
$$
DELIMITER ;

/* 조건에 따라 메뉴 출력 */
SELECT mn.name , mn.no, depth , parent, sequence
FROM    (
        SELECT no, @depth  AS vars_depth FROM menu WHERE no= '원하는메뉴번호'
        UNION ALL
        SELECT  hierarchy_connect_by_parent_eq_prior_id(no) AS no, @depth  AS vars_depth
        FROM    (
                SELECT  @start_with := '원하는메뉴번호',
                        @no := @start_with,
                        @depth  := 0
                ) vars, menu
        WHERE   @no IS NOT NULL
        ) fn
          JOIN    menu mn
            ON      mn.no = fn.no;
참고사이트
Share:

2018년 9월 26일 수요일

자기소개서 작성

필수항목

  • 내 전문 분야에 대해 흥미를 갖게 된 계기
  • 내 전문 분야와 관련된 과거의 경험(팀프로젝트,공모전,스터디 등)
  • 회사업무와 관련된 과거 경험(팀프로젝트, 공모전, 아르바이트 등)
  • 현재의 자신에 대한 설명(성격, 능력등)
  • 내 분야의 전문가로서 자신이 할 수 있는, 하고 싶은 일
모든 항목을 다 넣는 것이 Best
경험이 적거나 마이너스 요인이 될 가능성이 높은 경우에는 빼는 것이 좋다
1번과 5번은 필수!!

추가하면 좋은 사항
  • 개발 외 조직생활 경험
  • 동아리 경험
  • 지원하는 회사에 대한 제품에 대한 내용
  • 나의 현재 실력에 대한 내용
  • 나의 포부

자기소개서

  • 예시가 있는 근거로 주장을 해야한다.
  • 스토리를 넣는게 좋다.
  • 짧게 쓰는게 좋다.
  • 소제목을 넣어야하고 소제목만 봐도 의미를 알아야 한다.
  • 자소서를 봤을때 어떤사람인지 알수있게 쓰는게 좋다.
  • 지원하는 직무와 잘 맞아야 한다.
  • 자소서에 쓴 나와 실제의 나와 일치해야 한다.
다른사람과 비교해서 나의 장점을 부각해서 적어야한다.

기업별 자기소개서 특징

  • 대기업 채용 : 원하는 인재상이 정확히 정해져 있다. 인재상에 부합하는 인재상 중에서 가장 능력
    있는 사람을 뽑고 싶어함.
  • 중견기업 : 충성도가 1순위, 스펙을 많이 보는 경향, 일을 빠르게 많이 수행할 수 있는 능력
  • 스타트업 : 기업의 비전의 뜻을 같이, 알아서 잘크는, 자기주도적인 인재

검토

직무 70%, 직무 외 내용 30%

인사 담당자가 궁금해 하는것은 어떻게가 아니라 왜?이다(기술,퇴사등등)
Share:

Spring 프로젝트에서 ckEditor를 이용해서 이미지 업로드 구현

1. 기본 셋팅

  1. 사이트
  2. 설치
    • 다운로드 사이트에 접속해서 Full Package 를 다운받는다.
    • 압축을 해제한다.
    • 압축해제한 폴더를 webapp 폴더에 resources폴더를 만들고 복사한다.
  3. 리소스 폴더 매핑
    • ckeditor폴더의 리소스를 매핑하는 부분을 설정한다.
  4. <mvc:resources mapping="/resources/**" location="/resources/" />
    
  5. 기본 에디터 셋팅
    <!DOCTYPE html>
    <html lang="ko">
    <head>
     <script src="/resources/ckeditor/ckeditor.js"></script>
        <script>
        window.onload = function(){
           ck = CKEDITOR.replace("editor");
        };
        </script>
    </head>
    <body>
        <form>
            <textarea name="editor1" id="editor1" rows="10" cols="80">
                This is my textarea to be replaced with CKEditor.
            </textarea>
        </form>
    </body>
    </html>
    

2. 이미지 기본 업로드

  1. ckEditor config 설정을 변경한다.
  2. <script src="/resources/components/ckeditor/ckeditor.js"></script>
    <script>
        var editorConfig = {
            filebrowserUploadUrl : "/ckEditor/imgUpload", //이미지 업로드
        };
    
        CKEDITOR.on('dialogDefinition', function( ev ){
            var dialogName = ev.data.name;
            var dialogDefinition = ev.data.definition;
    
            switch (dialogName) {
                case 'image': //Image Properties dialog
                //dialogDefinition.removeContents('info');
                dialogDefinition.removeContents('Link');
                dialogDefinition.removeContents('advanced');
                break;
            }
        });
     window.onload = function(){
          ck = CKEDITOR.replace("editor", editorConfig);
     };
    </script>
    
  3. 리소스 폴더 매핑
    파일 저장을 아래와 같이 설정 했을 경우
  4. //파일 기본경로
    String defaultPath = request.getSession().getServletContext().getRealPath("/");
    //파일 기본경로, 상세경로
    String filePath = defaultPath + "resources" + File.separator + "ckEditorImg" + File.separator;
    
    파일을 저장한 위치와 요청한 url을 매칭시키는 설정을 추가한다.
    <mvc:resources mapping="/ckEditorImg/**" location="/resources/ckEditorImg/" />
    
  5. 서버에서 아래의 형식에 맞게 JSON으로 응답한다.

3. 드래그 이미지 업로드

  1. add-on 설치
    upload image 애드온은 '드래그앤드롭'을 가능하게 해주고, 나머지는 종속되는 프로그램
    프로그램을 다운받아서 ckeditor/plugins 디렉토리에 넣는다
  2. ckEditor config 설정을 변경
    <script src="/resources/components/ckeditor/ckeditor.js"></script>
    <script>
        var editorConfig = {
             filebrowserUploadUrl : "/ckEditor/imgUpload?type=image", //이미지만 업로드
             extraPlugins : 'uploadimage'
        };
     
     
        CKEDITOR.on('dialogDefinition', function( ev ){
            var dialogName = ev.data.name;
            var dialogDefinition = ev.data.definition;
     
            switch (dialogName) {
                case 'image': //Image Properties dialog
                //dialogDefinition.removeContents('info');
                dialogDefinition.removeContents('Link');
                dialogDefinition.removeContents('advanced');
                break;
            }
        });
     
        window.onload = function(){
             ck = CKEDITOR.replace("editor", editorConfig);
        };
    </script>
    
Share:

2018년 9월 19일 수요일

NHN 엔터테이먼트 안정적인 서비스 운영 - 설계에서 모니터링 까지


NHN에서 주관한 세미나에 참석했다
서비스를 운영하면서 설계하는 여러가지 방법과 운영하면서 신경써야 할 부분에대해 들을 수 있었다.
발표자료는 슬라이드쉐어에서 볼 수 있다.
세미나 정보도 올라오는 페이스북도 있다


Share:

2018년 9월 18일 화요일

2018년 8월 28일 화요일

2018년 8월 6일 월요일

2018년 7월 12일 목요일

자바 웹 프로그래밍 Next Step 2장 정리

도서

자바 웹 프로그래밍 Next Step 하나씩 벗겨가는 양파껍질 학습법 - 박재성
https://book.naver.com/bookdb/book_detail.nhn?bid=11037465

내용

  • 한번에 메소드 하나에만 집중한다.
  • 결과 값을 눈이 아닌 프로그램을 통해 자동화한다.
  • 테스트코드 중복을 제거한다.

지켜야할 항목

  • 각 단계의 개발 과정은 구현 -> 테스트를 통해 결과 확인 -> 리팩토링 진행
  • 메소드가 한 가지 책임만 가지도록 구현한다.
  • 들여쓰기는 깊이를 1단계로 유지한다.
  • else를 사용하지 마라.

JUnit

GitHub 

추가학습

실습

Share:

2018년 6월 12일 화요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 42일차(AOP, Argumentresolver)

AOP

종단관심 : 객체란 관련된 정보를 가지고 있다.
횡단관심 : 모든 메소드의 시작 전,후에 로그 처리를 하고싶다.

AOP는 종단관심만으로 처리 할 수 없는 부분을 처리해준다.
용어

  • joinPoint : 적용할 위치(횡단관심) -> 메소드 시작,끝 / Exception / 메소드를 감싸서(위빙)
  • advice : joinPoint에서 실행 할 코드
  • pointcut : 어떤 Advice를 어떤 joinPoint에 적용할지 설정
  • Aspect : joinPoint + advice + pointcut(위빙)
  • target

Argumentresolver

interceptor 다음에 실행된다.
메소드 안에 있는 파라미터의 타입을 지원해주는지 확인하여 값을 알아서 넣어주는 기능을 한다.
Handlermethodargumentresolver를 implements하면 메소드 2개를 오버라이딩 한다.
  • Handlermethodargumentresolver를 구현하는 클래스를 생성 후 설정에서 ArgumentResolvers를 추가한다.
  • supportsParameter 메소드는 지원을 하는지 안하는지 여부를 확인하는 메소드이다.
  • resolveArgument 메소드는 supportsParameter 메소드에서 return값이 true일 때만 실행되며 사용할 Argument를 셋팅하면 다른 클래스 파일에서 Argument를 사용하면 자동으로 값을 넣어준다.
  • 클래스 정보는 메모리에 1번만 올라가기 떄문에 '=='로 비교가 가능하다.
public class LoginUserInfoArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Class clazz = methodParameter.getParameterType();
        if(clazz == LoginUserInfo.class)
            return true;
        return false;
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        LoginUserInfo loginUserInfo = (LoginUserInfo)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return loginUserInfo;
    
@SpringBootApplication
public class LoginApplication{

   public static void main(String[] args) {
      SpringApplication.run(LoginApplication.class, args);
   }

   @Override
   public void addArgumentResolvers(List resolvers) {
      resolvers.add(new LoginUserInfoArgumentResolver());
   }

}
Share:

Spring Boot에서 정적, 동적 파일 실시간 반영

설정을 통해 파일에 변경이 있을때 자동 Realod 

1. POM.xml 에서 spring-boot-devtools를 추가한다.

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-devtools</artifactId>
 <scope>true</scope>
</dependency>

2. livereload enable 및 cache 옵션을 false ( application property )

Intellij 에서는 3~5번 추가로 설정

3. Action을 찾는 단축키 ctrl+shift+A로 registry를 검색 후 선택한다.

4. compiler.automake.allow.when.app.running를 선택

5. Settings -> Build -> Compiler에서 Build project automatically를 체크(파일 수정시 자동 빌드)

6, 프로젝트 재 시작

7. 파일 수정시 자동 반영 (5번 항목을 안했을 경우는 수동으로 빌드)

spring-loaded 플러그인으로 Realod(Spring boot 1.3 미만)

1. POM.xml 에서 Plugin springloaded 를 추가한다.

<plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 <dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>springloaded</artifactId>
   <version>1.2.8.RELEASE</version>
  </dependency>
 </dependencies>
</plugin>

2. 템플릿 캐시 끄기( application property )

3. Maven 패널에서 Spring-boot:run을 실행한다.(터미널 명령어는 mvn spring-boot:run)

4. 파일 적용

  • 정적 파일은 서버 재시작 없이 적용 
  • 동적 파일은 Build Project 실행후 확인

TIP

크롬 LiveReload 설치를 통해 변경이 있을때 브라우저 자동 Reload

https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei?hl=ko












Share:

2018년 6월 11일 월요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 41일차(APM, 운영)

CommandLineRunner

스프링이 구동되는 시점에 실행하고 싶은 코드가 있는 있으면 CommandLineRunner을 구현한다.
스프링 컨테이너에 관리하는 객체들을 관리하기 위해서 CommandLineRunner 의 run()메소드를 오버라이딩 하여 구현한다. CommandLineRunner를 구현하면 빈들이 메모리에 모두 올라간 후에 run()메소드를 호출한다. 기존 main()메소드를 사용안하는 이유는 기본 Main 메소드는 JVM이 실행하고 static하기 때문에 빈을 사용할수 없다.

APM

SCOUTER(어플리케이션 모니터링 서비스)

JAVA agent 와 HostAgent를 통해 JAVA와 OS에 대한정보를 수집한다.
오류내용와 요청횟수를 확인할 수 있다.

운영

운영을 하려면 배포도구(젠킨스등)와 운영모니터링(스카우터) 도구는 필수
무중단 시스템을 만들자






Share:

2018년 6월 9일 토요일

Spring Security 프로젝트 생성 및 로그인

1. SPRING INITIALIZR를 통해 아래와 같이 설정하고 프로젝트를 생성한다.

Maven에서 시큐리티 설정을 추가하고 싶으면 아래 goal을 추가한다.

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. 서버를 실행하고 브라우저에서 http://localhost:8080 으로 접속한다.

Spring Security는 Client에서 요청이오면 FilterChainProxy 라는 Bean에 의해 Security Filter를 거치게 된다.


Spring Security에 의해 로그인 페이지를 생성하지 않아도 기본적으로 제공해주는 로그인 화면이 보인다. 
로그인 정보는 어디서 확인할 수 있을까?
아이디는 user
비밀번호는 콘솔을 확인해보면 아래와 같이 비밀번호를 알려준다. 비밀번호는 서버 구동할떄마다 변경된다.












Share:

2018년 6월 8일 금요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 40일차(기타)

다 잘할수는 없지만 1가지라도 잘하자

사용자가 많아질 경우 WAS가 추가 될수 있는데 nginx 웹서버를 이용하면 구성하기 쉽다.

언어의 철학에 대해 이해하자. 어떤 이유에 대해 만들어 졌는지

개발자는 요구사항을 개발한다.
  • 개발전에 Architecture를 구현한다.
  • Architecture를 재사용 하여 다른 요구사항을 개발한다.
  • 구성되어 있는 Architecture는 최적의 Architecture다.
  • Framework
스프링 시큐리티는 웹 어플리케이션에서 제일 앞에서 막는다.
서블릿필터와 디스패처서블릿은 스프링 3.0전까지는 빈으로 등록되지 않았다.(설정부분에서 등록하기 때문)
스프링 3.0 이후에는 빈으로 가지고 올 수 있게 변경되었다.

프레임워크란 내가 개발한 것이 무언가에 의해서 컨트롤 당할때
라이브러리는 내가 이용해서 무언가를 컨트롤할떄

메소드 안의 구현을 보지말고 어떻게 인터페이스를 설계와 클래스의 관계등 설계를 보자.

추상화가 높은 부분부터 공부하고 나중에 낮은 부분까지 공부하자.

누군가 어려웠던 개발을 물어보면?
해본사람만 알수 있는 구조적인 부분을 설명하자.

누구한테 물어볼때 내가 시행착오를 겪은 내용을 정리해서 물어보자.

JPA에서 N+1쿼리 일때는 fetch join을 하자



Share:

2018년 6월 5일 화요일

[ERROR] null value was assigned to a property of primitive type setter of

필드에 @Column(nullable = true) 옵셥을 주고 값을 가지고 올때 해당 필드에 null인 값이 있는 데이터가 있으면 에러가 발생한다. 기본 유형에 null값을 할당 할 수 없어서 wrapper class를 변경해서 사용하여야한다.

stackoverflow 참고
https://stackoverflow.com/questions/3154582/why-do-i-get-a-null-value-was-assigned-to-a-property-of-primitive-type-setter-o/3154585#3154585
Share:

2018년 6월 4일 월요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 39일차(JPA)

Spring Boot JPA

Spring DATA JPA를 사용한다.
Spring DATA JPA ----> JPA ---> Entity Manager가 포함되어있어서 Entity Manager 객체를 만들어서 사용하지 않아도 된다.

엔티티 매니저도 객체이다. Heap메모리에 올라가 있다.

persist하거나 JQPL,Find(),을 사용하면 1차캐시에 저장된다.
요청을 할때 DB에서 읽어온 데이터는 1차캐시에 저장되어 있다. 같은 요청을 하면 1차 캐시에 데이터가 있는 데이터를 반환한다. 그래서 상황에 따라 LAZY로딩이 더 빠를수도 있다.

엔티티 작업은 트랜잭선 안에서 일어나야 한다.
스프링 부트에서는  @Transactional을 통해 트랜잭션을 관리한다.

엔티티는 SQL을 최대한 늦게 실행하려고 한다. 트랜잭션이 끝날때 실행한다.

실행 순서

1. 영속성을 부여하면 그 순간에 엔티티에 대한 스냅샷이 저장된다.
2. 그 상태에서 엔티티에 대한 데이터를 수정한다.
3. 트랜잭션이 종료될 때 스냅샷의 상태와 객체의 상태를 값이 변경된게 있으면 update가 실행된다. 값이 같으면 update가 실행되지 않는다.
4. remove를 실행하면 사라질 객체이기 때문에 중간에 update문이 있어도 생략된다.

엔티티 매핑

1:1 관계는 하나로 합쳐도 되는 관계이다.(성능이나 관리떄문에 나누는 경우도 있다)
어디에 FK가 있든지 상관없다 하지만 FK를 가지고 있는 테이블에서 나중에 N관계를 가질 수 있다.

N : N
데이터베이스 에서는 N:N관계는 없다. 데이터베이스에서는 중간에 관계테이블이 있어야 한다.

FK가 있는 엔티티가 주인인다.
@JoinColumn을 적어야 한다. @JoinColumn이 들어가면 단방향 관계이다.
FK에 참조 당하고 있는 엔티티에서 MappedBy옵션은 FK가 있는 엔티티의 정의된것을 이용해서 찾겠다는 뜻이다.

N;1관계에서
@JoinColumn만 사용하는 것은 단방향
MappedBy옵션까지 사용하면 양방향 참조다.

N:N관계에서
@JoinTable를 사용한다.

cascade옵션은 참조하고 있는 엔티티도 같이 영속성을 부여하는 것이다.

단방향
한쪽에서만 참조한다.

양방향
서로가 서로를 참조한다.

fetch조인은 한번에 쿼리를 실행하지만 중복된 데이터가 나올수 있어서 중복된 데이터를 없애는 distinct 를 사용한다.

내부적인 구현보다는 논리적인 구조에 대해 이해하자.
다른사람의 소스코드에서 메소드안의 코딩 보다는 클래스,인터페이스의 대한 관계(설계)를 보자

Share:

2018년 5월 31일 목요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 37일차(RabbitMQ, WebSocket)

RabbitMQ

Queue형식이고 FIFO다.
이벤트 프로그래밍에서 주로 사용된다.

여러가지 방식이 있다.
publis / Subscribe(발행자,구독자) 패턴: 구독하고 있는 사람들에게 모두 발송한다.
topics 패턴 : 패턴에 의해 읽어간다.(한큐에 같은 패턴의 여러 클라이언트가 붙어있을 경우는 먼저 호출한 클라이언트에게만 발송된다.)

WebSocket

어떤 브라우저에서는 WebSocket을 지원 안할수 있다. 그래서 SockJS를 사용한다.
연결 흐름은 HTTP연결후 WebSocket연결을 한다.

크롬 요소검사에서 Network - WS - Frame 메뉴에서 통신하는 메시지를 볼수 있다.
오래동안 사용하지 않으면 연결이 끊길수 있다. 백그라운드에서 특정 주기마다 통신해야한다.

Srping SockJS

afterConnectionEstablished : 현재 연결된 파라미터가 들어온다.(클라이언트 연결 개수만큼 실행)
afterConnectionClosed : 종료될때 사용된다.

로그인 사용자 채팅

웹소켓도 시큐리티가 필요하다.
HttpSessionHandsakeInterceptor()를 통해 WebSocketSession에 로그인 정보를 저장후 사용하는곳에서 session정보를 불러와 사용한다.

Share:

2018년 5월 28일 월요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 35일차(Test, 공부방법)

Controller 테스트

@RunWith(SpringRunner.class)
@WebMvcTest(BoardController.class)
  1. RunWith는 Runner를 구현한 객체를 이용해서 Test를 수행한다.
  2. SpringRunner는 내부적으로 ApplciationContext를 가지고 있고, 해당 Test객체도 Bean으로써 등록된다.
  3. @WebMvcTest : Spring MVC를 테스트를 사용하기 위한 애노테이션이다.
    Controller는 여러개의 Service를 사용한다. Service의 구현과 상관없이 Controller를 테스트해야한다.
    이것이 올바른 Controller의 단위테스트가 된다.
    @WebMvcTest(BoardController.class) : BoardController를 테스트하겠다.
@Autowired
private MockMvc mvc;
@WebMvcTest를 사용하면 MockMvc를 주입받을 수 있다. WAS를 실행하지 않고, Controller를 실행하도록 도와준다.

mvc.perform(get("/boards").with(new RequestPostProcessor() {
    @Override
    public MockHttpServletRequest postProcessRequest(MockHttpServletRequest mockHttpServletRequest) {
        User user = new User();
        user.setId(1L);
        user.setName("홍길동");
        mockHttpServletRequest.setAttribute("loginUser", user);
        return mockHttpServletRequest;
    }
}))
.andExpect(status().isOk()) // 200 OK면 성공
.andExpect(model().attribute("list", page)) // ModelMap에 값을 담으면, 템플릿까지 값이 전달된다. Controller에서 ModelMap에 "list"란 이름으로 값을 담았는데, 그게 page와 같은지 본다.
.andExpect(content().string(containsString("test1"))); // content()는 템플릿엔진을 통해 랜더링한 결과에 "test1"이라는 문자열이 있는지 본다.
}
mvc는 Spring MVC가 controller를 실행하는 것을 시뮬레이션 한다. perform메소드는 controller의 특정메소드를 get, post, put, delete등으로 실행하도록 한다.
@MockBean
BoardService boardService;
BoardController가 의존하고 있는 Service를 Mock으로 설정한다.
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>5.0.4.RELEASE</version>
    <scope>test</scope>
</dependency>
테스트를 할 때 Spring security와 관련된 부분을 도와주는 라이브러리를 추가한다.
Test메소드를 만든다.
@WithMockUser(username = "admin@gmail.com", authorities = {"USER"})
로그인한 사용자의 username과 권한을 지정할 수 있다.
// with()부분은 Controller가 아닌 인터셉터에서 로그인한 정보를 저장하는데,
// 이 부분이 템플릿에서 사용하기 때문에 request에 로그인한 정보를 넣어줌.
ResultActions content1 = mvc.perform(get("/boards/1")
    .with(mockHttpServletRequest -> {
        User user = new User();
        user.setId(1L);
        user.setName("홍길동");
        mockHttpServletRequest.setAttribute("loginUser", user);
        return mockHttpServletRequest;
    }))
    .andExpect(status().isOk())
    .andExpect(content().string(containsString("content1")));
인터셉터에서 정보를 넣어주는 로직이 있을때 사용한다.

공부방법

  1. 책과 문서를 보며 어려웠던 부분과 중요한 부분을 체크하며 읽는다. 흐르듯이!(메모장등에 체크)
  2. 깃허브에서 소스를 보면서 내가 그 프로젝트를 만든다고 생각하고 소스를 본다.
  3. 궁금한점은 검색하면서 좋은 소스들을 찾아서 본다.
  4. 샘플코스를 작성한다. 라이브러리 및 프로그램이 이미 정해져있다.
  5. 잘하는 프로그래머한테 리뷰를 요청한다.
  6. 다른사람에게 설명한다.
  7. 개발할때 스스로에게 설명하면서 만든다.(이해 안가는게 있으면 적어놓던지 찾아본다.)







Share:

2018년 5월 22일 화요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 32일차(Exception처리)

Exception 화면

  • Exception이 발생하면 사용자에게 에러화면을 보여줘야 한다.

Exception 처리

  1. 처리하는 메소드에서 try cath문을 통해 처리하면 프로그램이 죽지않지만 잘못된 값을 가질수 있다. 
  2. 메소드에서 throws Exception명을 통해 Exception을 넘겨줘서 호출한 곳에서 try - catch 처리를 한다.
  3. Controller, Service, Repository에 throws Exception명을 처리하여 Dispatcherservlet에 에러 처리를 넘긴다. ExceptionHandler를 이용해 에러 처리를 한다.(Web api와 일반 호출하는 방법에 대해 최소 2개이상 에러 처리를 해야한다.)
  4. Excpetion처리를 위한 에러 처리를 하는 전용 클래스를 만든다.RuntimeException을 상속하여 구현한다. 처리하는 메소드에서 try - catch - throw - throws 처리하여 에러전용 클래스에 throw한다. Controller, Service, Repository마다 에러전용 클래스에 throw한 내용을 바탕으로 ExceptionHandler에서 log를 남긴다. 
잘못된 값을 넘겨줘서 연산이 일어나는 것은 매우 심각하다.
Share:

2018년 5월 17일 목요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 30일차(Test)

1. Test

Spring Boot는 격리시켜서 테스트 한다.

1번째 방법
  • 설정파일에 설정된 모든 빈들을 읽어서 사용한다.
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class UserRepositoryTest

2번째 방법
  • @Repository와 @Entity만 생성한다.
  • @SpringBootApplication에서 @Autowired가 있으면 주입이 안될수도 있다. @Autowired(required = false)로 지정해주면 된다.
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryTest

2. Jenkins(CI)

https://jenkins.io/
java와 빌드도구가 설치되어 있어야 한다.

주기적으로 변경여부를 확인할 수 있다.

2-1. 젠킨스 war파일 실행

젠킨스 war파일 설치 젠킨스 포트 변경하여 실행하고 로그 저장
java -jar /Users/mac/programing/CI/jenkins/jenkins.war --httpPort=9999 > /Users/mac/programing/CI/jenkins/app.log 2> /Users/mac/programing/CI/jenkins/error.log &
실행 확인
ps -ef | grep jenkins

2-2. 비밀번호 보기

localhost:9999 접속
접속시 보이는 경로 shell cat명령어로 확인후  비밀번호 입력

2-3. 작업 생성하고 빌드하기

깃 저장소 추가하고 저장

빌드실행하기
left menu: Build now 선택
Console Output에서 확인

빌드 실행옵션 변경하려면
left menu: 구성 선택
build항목에서
Excute Shell 선택후 gradle build -x test 입력

자동화 만들기
sftp -i 계정명@아이피
put명령어 사용하여 jar 업로드
종료

ssh 접속
jar파일 실행
종료


- 알면 좋은것들
PMD : 코드 품질 관리
linux expect : CLI 상호작용 자동화 프로그램
fabric : http://tech.whatap.io/2015/10/15/install-whatap-on-multiple-linux-simultaneously/

Share:

Spring boot에서 @SpringBootTest와 @DataJpaTest 차이점

Test

Spring Boot는 격리시켜서 테스트 한다.

1번째 방법
  • 설정파일에 설정된 모든 빈들을 읽어서 사용한다.
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class UserRepositoryTest

2번째 방법
  • @Repository와 @Entity만 생성한다.
  • @SpringBootApplication에서 @Autowired가 있으면 주입이 안될수도 있다. @Autowired(required = false)로 지정해주면 된다.
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryTest

Share:

2018년 5월 15일 화요일

Docker 정리

Docker 명령어

https://docs.docker.com/get-started/

#도커 이미지 목록
docker images

#도커 컨테이너 전체 목록
docker container ls -all

#도커 실행중인 목록
docker ps

#도커 컨테이너 전체 목록
docker ps -a

#도커 죽이기
docker kill 컨테이너id

#도커 삭제하기
docker rm 컨테이너 id

#도커 이미지 삭제하기
docker rmi 컨테이너 id

#Compose 파일로 컨테이너 생성(-d : 백그라운드 실행), docker-compose.yml파일이 있는곳 실행
docker-compose up -d

#Compose 파일에 정의 된 서비스 용 컨테이너 제거
docker-compose down

#도커 이미지 생성 및 태그 부여(현재지점)
#tag명:버전 (입력하지않으면 latest로 입력되고 생성할대 마다 이미지가 계속 쌓인다)
docker build -t 태그명 .

#도커 이미지 실행
#-d : 백그라운드 실행
docker run -p 내컴퓨터포트:도커연결할포트 태그명 -d

#로그보기
docker logs 컨테이너id

#도커로그인
docker login

#도커이미지 복사하기
docker tag 내컴퓨터에있는이미지명 계정명/생성할이미지명:생성할태그명

#도커hub에 이미지 업로드
docker push 계정명/이미지명:태그명

#도커서비스 목록
docker service ls
docker service ps 서비스명

#도커 swam시작하기(라운드로빈가능)
docker swarm init

#docker swam join문을 실행하여 다른 컴퓨터와 연결할 수 있다.
docker swarm join --token 토큰키 아이피:포트

#docker yml설정 파일 기반으로 swam실행하고 이름 지정하기
docker stack deploy -c docker-compose.yml swam생성이름
#docker-compose.yml 예제
version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"
    networks:
      - webnet
networks:
  webnet:

#docker swam 서비스중인 목록
docker service ls

#docker swam 이름으로 실행중인 서비스 목록 보기
docker service ps swam생성이름

#docker swam 종료
docker swarm leave --force

#docker swam 제거
docker stack rm swam생성이름
Share:

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 29일차(docker)

Docker 명령어

https://docs.docker.com/get-started/
#도커 이미지 목록
docker images

#도커 컨테이너 전체 목록
docker container ls -all

#도커 실행중인 목록
docker ps

#도커 컨테이너 전체 목록
docker ps -a

#도커 죽이기
docker kill 컨테이너id

#도커 삭제하기
docker rm 컨테이너 id

#도커 이미지 삭제하기
docker rmi 컨테이너 id

#도커 이미지 생성 및 태그 부여(현재지점)
#tag명:버전 (입력하지않으면 latest로 입력되고 생성할대 마다 이미지가 계속 쌓인다)
docker build -t 태그명 .

#도커 이미지 실행
#-d : 백그라운드 실행
docker run -p 내컴퓨터포트:도커연결할포트 태그명 -d

#로그보기
docker logs 컨테이너id

#도커로그인
docker login

#도커이미지 복사하기
docker tag 내컴퓨터에있는이미지명 계정명/생성할이미지명:생성할태그명
#도커hub에 이미지 업로드
docker push 계정명/이미지명:태그명

#도커서비스 목록
docker service ls
docker service ps 서비스명

#도커 swam시작하기
docker swarm init

#docker swam join문을 실행하여 다른 컴퓨터와 연결할 수 있다.
docker swarm join --token 토큰키 아이피:포트

#docker yml설정 파일 기반으로 swam실행하고 이름 지정하기
docker stack deploy -c docker-compose.yml swam생성이름
#docker-compose.yml 예제
version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"
    networks:
      - webnet
networks:
  webnet:

#docker swam 서비스중인 목록
docker service ls

#docker swam 이름으로 실행중인 서비스 목록 보기
docker service ps swam생성이름

#docker swam 종료
docker swarm leave --force

#docker swam 제거
docker stack rm swam생성이름
Share:

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 28일차(docker)

  • Docker
    • 도커이미지 : 도커에 의해서 설치되는 것
    • 명령어
      • docker images : 도커 이미지 목록
    • 설정
      • https : 443포트사용
      • links : 도커의 아이피는 항상 변경되서 다른 도커를 연결할때 links라는 옵션을 사용한다.
      • volumes : 설정파일을 도커안에 복사해서 사용한다.
    • docker-compose.xml : 어떻게 실행할지 아는 파일
Share:

2018년 5월 14일 월요일

2018년 5월 13일 일요일

패스트캠퍼스 개발자 커리어 컨퍼런스

이직을 꿈꾸는 개발자들에게 선배 개발자들이 진솔하게 전하는 개발자 이야기
# 인사 담당자가 말하는 개발자 채용
# 개발자로서의 성장
# 한국 개발 커리어의 현실
에 대한 내용을 얘기해보는 컨퍼런스다.




Session 1. 인사담당자가 말하는 개발자 채용

1.1 개발인력의 공통특징/특성 및 채용관점에서 본 개발인력에 대하 요구역량/성장경로


  • 시간이 지날 수록 아래로 가서 역량을 펼쳐라
  • 본인이 가치창출, 희소성, 모방의 어려움, 비 대체성이 있는지 확인하라(by 제임스반)
  • 30대 중반부터 생산성이 하락
  • 요즘은 역량기반으로 채용
  • 우수 S/W
    • 프로그래밍 능력, 도메인지식, 커뮤니케이션
    • 문제가 주어지는 순간 머리속에 그려짐
    • 직무간(도메인) 전환도 빠르게 적용
    • 의사 소통이 중요(안좋으면 좋은 인사고과, 좋은 급여를 받기 힘듬)
    • 개발 업무 수행시 요구 사항 분석에 많은 시간을 투자
  • 채용 면접 평가 기준
    • 신입 : 기본기 위주 skill
    • 박사 : 지식+스킬 기반으로 프로젝트 등 새로운 문제해결 시도 및 어떤결과를 냈는지 평가
    • 경력 : 현재 사용 가능한 skill
  • 리더 역량
    • 기술+인간관계+관리능력
  • 스택을 넓히고 도메인을 깊이
  • 개발자에게 동기부여
    • 레벨 테스트
    • 주축 개발자가 20~30%정도의 시간을 투자해 교육 및 보상
  • 관련없는 분야 이직시
    • 트렌드를 보자
    • 기존에 가지고 있던 도메인에 대한 개인공부
  • 내역량을 드러내는 방법
    • 다른사람과 다른 나의 고유한 내적 특성
    • 오픈소스기여(재밌어서 하는게 좋다)
  • 기업별 보는 스킬
    • 대기업: 한개의 도메인의 깊이
    • 중견기업 : 한개를 잘하지만 다른것도
    • 회사의 성향을 보자

Session 2. 개발자, 직장인? 직업인?

개발자로서 실력을 키워 성장하는 법과, 개발팀 직원으로서 좋은 인사고과를 받고 살아남아 승진하는 법은 다릅니다.
개발 실력과 회사 생활 양쪽을 모두 신경써야하는 직장인 개발자를 위한 성장 꿀팁을 공개합니다.

2.1 개발과 업무의 환경에서 - 레이니스트 안드로이드 개발자 김범준

  • 개발에 빠진 이유?
  • 개발이 즐거운가?
  • 일이 즐겁나요? 반복적인 일, 쉬운일

2.2 어떻게 하면 추천받는 개발자가 될 수 있을까? - 카카오 이경일

  • 경력공채, 수시채용 -> 사내추천
  • 추천해준 사람을 통해 면접관의 관심사를 알 수 있다.
  • 같은조직에 한해 서류전형이 통과될 수 있다.
  • 리스크가 적다.
  • 추천받는법(2가지이상)
    • 현 동료와 좋은 고나계를 유지하라(커뮤니케이션, 협엽)
    • 본인의 업무에 최선을 다하라(전문성, 도메인)
    • 했던일을 적극적으로 표현(깃허브 세미나, 블로그)
    • 컨퍼런스 참석
    • 4가지를 충족해도 인성이 나쁘면? 별로다
    • 이사람에게 일을 맡기면 해결할 수 있는 인식이 중요

2.3 관리자와 개발자 - 리멤버 CTO 임세준

  • 시간이 지나면 관리자?
  • API 개발이 즐거웠다
    • 메소드 시그니처
    • 클래스 나누기
    • 라이브러리
    • 성능
  • 기술리더 : 코딩할 시간이 없다
  • 조직관리
  • 문화 프로세스
    • 업무프로세스 : 스크럼, pp, 코드리뷰
    • 품질관리 : 단위테스트, QA
    • Risk Taking : 신기술 도입장려
  • 채용
    • 채용기준
    • 채용프로세스
    • 다양한 리쿠르팅 활동

QnA


  • 같이 일하기 싫은사람?
    • 독단적인 사람
    • 방어적인 태도
    • 근거와 공감능력이 필요하다
    • 생각에 대한 논리적인 근거가 필요하다
  • 개발성장과 도움
    • 문제해결능력(논리적 상황에서 관련된 사항 보는 능력)
    • 내가한 행동을 동료에게 넘기고 피드백 요청
  • 다른분야로 이직
    • 토이프로젝트가 필요
    • 빠른 습득력 필요
  • 연봉과 역량 그리고 업무,
    • 서비스를 빨리 끝내면 그것은 인센티브
    • 역량은 윗분들에게 리뷰요청등 피드백 요청해서 알리자 연봉협상에 좋다
    • 나의 관심사를 적용해 개발

Session 3. 내가 보는 한국 개발 커리어의 현실

내가 개발자로서 언제까지 살아남을 수 있을까?
핫한 기술 스택 열심히 배우면 이직/승진이 쉬워질까?

3.1 17년을 돌이켜보며 - 네이버 조은

  • 언어를 잘하는 것이 아니라 프로그래밍을 잘해야 한다
  • 내가 잘하는것 못하는것, 어디에 초점을 맞출까?
  • 면접을 보면 나를 알 수 있다
  • 레퍼런스 번역을 하며 공부하는것도 방법
  • 회사도 중요하시만 팀에서 하는일도 중요

3.2 야생에서 살아남는법 프리로 사는법 하이퍼커넥트 - 최완복

  • 짧은 프로젝트 위주의 경력
  • 야생 생종법
    • 기간, 공백
    • 계약해지
    • 평판이 중요하다
    • 커뮤니티 활동
    • 일감확보(기존 거래처 오퍼 및 커뮤니티에서 검색)
    • 기술력 확보
      • 공격적인 기술셋
      • 짧게 여러 프로젝트에 적용
    • 커뮤니티로 배우기
      • 열정을 충전
      • 어려운 문제 해결
    • 오픈소스 기여
      • 가르키는것이 최고 학습법
    • 관계 맺기/유지
      • 커뮤니티 : 신기술, 행사
      • 해커톤 : 골라서 잘가자
    • 새로운 시도 지속하기

3.3 20년차 개발자로 살아남는법 - CTO 굿닥 신현묵

  • 벤처 -> SI -> 의료 -> 헬스케어 전문가
  • 27년차 개발자
  • 20년차 개발자 볼수있나?
    • 고참 테크니션이 필요한 곳
    • 고참 경력자를 인정하는 곳
  • 20년차
    • 분석, 설계
    • 기술적 로드맵 결정 참여
    • 기술적인 이슈 결정 참여
    • 비즈니스에 대한 이해도 상승
  • How?
    • 다양한 경험
      • 초기 5년이 중요
      • 대기업 별로(4~7년은 까지만)
      • 커뮤니티 오픈소스 활동
    • 리뷰 활동
    • 뛰어난 아키텍처 동료
    • 운도 중요
    • 10년이상개발자는 인건비내리자 
    • 망해보자
  • 살아남는법? 직장인? 개발자?
    • 사장님
    • 개발팀장
    • CTO
    • 솔루션 or 프레임워크 주요커미터
    • 변신
      • IT출판사 직원
      • 컨설턴트 기술 영업직
      • 직장인
    • 대화가 잘되고 조언주는 사람

QnA

  • 연 8~10억정도 솔루션이면 혼자해도 된다
  • 문제에 대한 솔루션은 없는데 고민이 있을때 이직한다
  • 이직
    • 경영자가 삽질할때
    • 내가 필요 없을때
  • 주니어/시니어, A/B급
    • 업무이외 개발을 하는가? 즐기면서 하고있는가?
    • 잘만드나? 가독성 Test, 성능, 설계
  • 개발자에게 서포트?
    • 개발자가 뭘 원하는가? 맥북..
    • 연봉
    • 자율출퇴근
    • 시간적인 자유로움
    • 철학, 비전 공감이 필요하다
    • 삽질안하고 동일한 행동 반복 안하게하고 CTO가 좋은 결정하면 된다.
Share:

2018년 5월 12일 토요일

github에서 특정 파일 삭제 하기

특정파일(yml파일) 모든곳에서 히스토리 삭제

git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch *.yml' --prune-empty -- --all # 모든 히스토리에서 해당 파일 삭제
git push origin --force --all

원격 저장소와 로컬 저장소에 있는 파일을 삭제

git rm file1.txt
git commit -m "remove file1.txt"
git push

원격 저장소의 파일 삭제

git rm --cached file1.txt
git commit -m "remove file1.txt"
git push

* 참고 사이트

Share:

AWS Linux에서 JDK 1.8 , Tomcat 8.5 , Maven 설정하기 (mac기준)

1. 인스턴스 생성

Linux를 선택하고 기본으로 셋팅하고 만든다.(고정 아이피도 설정)

보안그룹에서 8080, 22, 80번 포트를 열어둔다.

2. 터미널에서 키에 대한 권한설정(ec2-user계정)

#파일권한 변경
chmod 400 키파일디렉토리/lalwrj.pem

AWS에 접속하기
ssh -i ~/키파일디렉토리/pem키파일 ec2-user@아이피주소

아이피주소 : IPv4 Public IP

3. 루트계정 만들기(ec2-user계정)

3.1. 루트관리자 계정 만들기(root)

sudo passwd
처음엔 일반사용자 패스워드 입력.
그 다음엔 루트사용자 패스워드 입력.

root 관리자로 로그인 테스트
su -

3.2. ssh 터미널에 루트관리자로 로그인하기 위한 ssh 환경설정파일 편집

vi 에디터로 파일 열기( vi 대신 nano 사용 가능)
vi /etc/ssh/sshd_config  또는 nano /etc/ssh/sshd_config

PermitRootLogin without-password 라인을 아래와 같이 수정
PermitRootLogin yes

StrictModes yes 라인을 아래와 같이 수정(주석처리로 비활성화. 앞에 #만 붙이면 됨)
#StrictModes yes

3.3. 아마존 AWS로 부터 받은 일반사용자 암호화키를 루트관리자 계정에도 복사

sudo cp /home/ec2-user/.ssh/authorized_keys /root/.ssh

* 위의 경로에서 /home/ec2-user는 윈도우 OS로 따지면 "C:\사용자\홍길동" 과 같음.
따라서, 여기서 ec2-user는 아마존에서 인스턴스 생성시 자동으로 만들어진 사용자 계정의 기본 홈 폴더임.
명칭이 다른 경우엔 당연히 /home/ 뒤에 해당 명칭을 입력해줘야 함.
.ssh/authorized_keys는 보안을 위한 개인 암호화키가 해당 디렉토리에 들어있음을 표시.
따라서, /home/ec2-user/.ssh/authorized_keys를 /root/.ssh 디렉토리에 복사하면 루트관리자 로그인시 일반사용자 로그인처럼 해당 암호화키를 사용해서 로그인할 수 있는 것

3.4. ssh 서비스 재시작

service sshd restart

4. Jdk 설치(root 계정)

4.1 wget 으로 설치

다운로드
wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u171-b11/512cd62ec5174c3487ac17c61aaa89e8/jdk-8u171-linux-x64.tar.gz"
압출풀기
tar xvzf jdk-8u171-linux-x64.tar.gz 

4.2 FTP 설치

http://www.oracle.com/technetwork/pt/java/javase/downloads/jdk8-downloads-2133151.html

FTP 프로그램에서 root로 접속후 파일 업로드

압축 풀기
tar xvzf jdk-8u171-linux-x64.tar.gz

파일 이동
mv jdk1.8.0_171 /loal/java/

버전확인
java -version
javac -version
기존 설치 버전이 계속 사용되면 제거 후 다시 확인

rpm -qa | grep jdk
yum remove jdk명

5. Tomcat 설치(root 계정)

https://tomcat.apache.org/download-80.cgi
tar xvzf apache-tomcat-8.5.31.tar.gz
파일 이동
mv apache-tomcat-8.5.31.tar.gz /local/tomcat8.5

tomcat 설치후 EC2 의 Security Group 에서 inbound 에 Custom TCP Rule 을 추가해 Port Range 에 8080 을 추가

6. Maven 설치(root 계정)

다운로드
wget http://mirror.apache-kr.org/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz
압출풀기
tar xvzf apache-maven-3.5.4-bin.tar.gz
파일 이동
mv apache-maven-3.5.4 /local/apache-maven-3.5.4

7. JDK, Tomcat, Maven 환경변수 설정(ec2-user계정)

bash_profile 파일 수정
vi ~/.bash_profile
마지막에 하단에 내용 입력
export JAVA_HOME=/local/java/jdk1.8.0_171
export MAVEN_HOME=/local/maven/apache-maven-3.5.4
export CATALINA_HOME=/local/tomcat8.5
export CLASSPATH=.:$JAVA_HOME/jre/lib/ext:$JAVA_HOME/lib/tools.jar:$CATALINA_HOME/lib/jsp-api.jar:$CATALINA_HOME/lib/servlet-api.jar PATH=$PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
환경변수 적용
source ~/.bash_profile

8. 각 계정별 UTF-8 인코딩 설정해 한글 이슈 해결(ec2-user계정)

bash_profile 파일 수정
vi ~/.bash_profile
UTF-8인코딩 설정
LANG="ko_KR.UTF-8"
LANGUAGE="ko_KR:ko:en_US:en"
설정정보 적용
source ~/.bash_profile
env명령어로 확인
env

9. 접속 

톰캣 실행
/local/tomcat8.5/bin/startup.sh

AWS주소:8080 로 접속
Share:

2018년 5월 10일 목요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 26일차(Web API, 소켓)

  • Web API
    • RestTemplate
    • WebClient(qlehdrl)
  • 소켓
    • HTTP통신으로는 안되고 웹소켓을 사용해야 한다.
    • websocket, stomp, sock.js
    • 웹소켓을 지원해주지 않는 브라우저를 위해 라이브러가 있다.
      자바스크립트 라이브러리 : socket.io
      스프링 라이브러리 : sock.js
    • subscribe는 서버에게 특정 주소로 구독을 요청한다. 서버는 클라이언트가 요청한 주소를 알고있다. 서버한테 특정 주소로 메시지가 오면 구독한 클라이언트에게 전송한다.
    • spring에서 /app 경로는 생략한다.
    • SimpMessagingTemplate는 메시지를 보내기위한 객체
    • 채팅방에 접속할때 연결되고 나가면 연결이 끊긴다. 페이지를 나가도 연결을 유지하려면 원페이지로 만들어야 한다.(프론트 프레임워크를 통해서)
Share:

2018년 5월 9일 수요일

Spring JPA

EntityManager

  • EntityManager는 데이터베이스 연결이 꼭 필요한 시점까지 커넥션을 얻지 않는다.
  • 트랜잭션을 시작할 때 커넥션을 획득한다.(@Transactional이 없으면 EntityManager를 관리하지 못한다.)
  • 하이버네이트를 포함한 JPA구현체들은 EntityManagerFactory를 생성할 때 커넥션 풀도 만든다.
  • 스프링 부트에서는 필터(OSIV) - DS - C - S - R - DB 순으로 처리된다.
    필터(OSIV) - DS - C 에서는 트랜잭션이 ReadOnly이다.
    S - R - DB 단계에서는 읽고 쓰기가 가능하다.
    레이지 로딩 사용하려고 필터단계에서 트랜잭션이 시작된다.

영속성 컨텍스트(Persistence context)

  • 엔티티를 영구 저장하는 환경
  • 논리적인 개념에 가깝다. 
  • 영속성 컨텍스트는 EntityManager를 생성할 때 하나 만들어진다. 그리고 엔티티 메니저는 영속성 컨텍스트에 접근할 수 있고, 영속성 컨텍스트를 관리할 수 있다.

영속성 컨텍스트 특징

  • 영속성 컨텍스트와 식별자 값. : 영속성 컨텍스트는 엔티티를 식별자 값으로 구분한다. 영속상태는 식별자 값이 반드시 있어야 한다.
  • 영속성 컨텍스트와 데이터 베이스 저장 : JPA는 보통 트랜잭션이 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영한다.이를 flush라고 한다.
  • 영속성 컨텍스트가 엔티티를 관리할 때의 장점 : 1차 캐시, 동일성 보장, 트랜잭션을 지원하는 쓰기 지연, 변경감지, 지연 로딩

엔티티 조회, 입력

영속성 컨텍스트는 내부에 캐시를 가지고 있는데, 이를 1차 캐시라 한다. 영속상태의 엔티티는 모두 이곳에 저장된다.
1차 캐시의 키는 식별자 값이다. 이미 1차캐시에 있을 경우 db에서 조회하지 않고 캐시에서 조회하게 된다.
ex> em.persist(member); 가 호출될 경우 1차 캐시에 저장된다.

ID를 자동생성으로 셋팅하면 DB에 insert가 먼저 일어나고 키를 얻어온다. 자동생성 하지 않으면 DB에 INSET하지 않고 ID를 가지고 1차캐시에 저장된다.

엔티티 수정

엔티티가 수정된 것을 감지하여 db내용을 수정하는 것을 변경감지라한다.
jpa는 영속성 컨텍스트에 보관할 때 최초 상태를 복사해서 저장해둔다. 이를 스냅샷이라 한다. 그리고 플러시 시점에 스냅샷과 엔티티를 배고해 변경된 엔티티를 찾는다.
변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 sql저장소에 보낸다.
쓰기 지연 저장소의 sql을 데이터베이스 보낸다.

변경관리는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용된다.

엔티티 생명주기

  • 비영속(new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
  • 영속(managed) : 영속성 컨텍스트에 저장된 상태
  • 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제(removed) : 삭제된 상태




User user = new User();
위의 user객체는 비영속 상태다.

EntityManager를 통해 엔티티를 영속성 컨텍스트에 저장할 경우 영속 상태가 된다.
영속성 컨텍스트에 위해 관리되는 상태를 영속상태라 한다.
영속상태라는 것은 영속성 컨텍스트에 의해 관리된다는 것을 의미한다.

조회된 엔티티도 영속성 컨텍스트가 관리하는 영속 상태다.

EntityManager가 관리하던 엔티티를 더이상 관리하지 않으면 준영속상태가 된다.
em.detach()를 호출하면 된다. 혹은 em.close()를 호출해서 영속성 컨텍스트를 닫거나 em.clear()를
해서 영속성 컨텍스트가 초기화 해도 영속성 컨텍스가 관리하던 엔티티가 준영속 상태가 된다.

em.detach(user);
삭제는 em.remove(user); 를 이용해서 삭제할 경우이다.

준영속

영속 상태의 엔티티를 준영속 상태로 만드는 방법

  • em.detach(entity) : 특정 엔티티를 준영속 상태로 전환한다. 1차 캐시부터 쓰기 지연 sql저장소까지 해당 엔티티를 관리하기 위한 모든 정보가 제거된다.
  • em.clear() : 영속성 컨텍스트를 초기화 한다.
  • em.close() : 영속성 컨텍스트를 종료한다.

준영속 상태의 특징

- 거의 비영속 상태에 가깝다.
- 식별자 값을 가지고 있다.
- 지연 로딩을 할 수 없다.

merge()를 통해 병합할 수 있다.

준영속 상태의 엔티티를 다시 영속상태로 변경할 경우 merge()를 사용한다.

ex: User user = em.merge(user);

영속 엔티티의 동일성 보장 

같은 id의 값을 가진 엔티티를 조회하면 == 를 이용했을 경우 참이 나온다. 이를 동일성이라한다. 이는 실제 인스턴스가 같다는 것을 의미한다.

트랜잭션 지연쓰기

ex> em.persist(user1); em.persist(user2); 와 같이 여러번 영속성을 부여한 다음 트랜잭션을 커밋할 때 한꺼번에 sql이 실행된다. 이를 트랜잭션을 지원하는 쓰기 지연이라고 말한다.
이를 flush라고 한다.

모아둔 쿼리를 한번에 전달해서 성능을 최적화 할 수 있다.
Share:

스프링에서 검증 @Valid 와 BindingResult

@Valid

  • Dispatcherservlet이 메소드안에 선언된 객체를 만들어주고 값을 넣어준다.
  • @Valid선언된객체에 설정을 바탕으로 검사후 BindingResult에 담아준다.
  • 에러를 발생하려면 FieldError객체를 만들어서 BindingResult에 넣어준다.
  • 태그에서 object에 담긴 객체의 프로퍼티를 filed *{필드이름} 형식으로 사용한다
  • errors는 BindingResult에 있는 에러값을 출력해준다.
  • BindingResult.hasErrors : 에러가 있는지 검사한다.
  • lombok, thymeleaf를 사용하여 사용한 예제이다.
@Setter
@Getter
@ToString
public class UserJoinForm {

    @NotNull
    @Size(min = 2, max = 10)
    private String name;
    @NotNull
    @Size(min = 2, max = 50)
    private String email;
    @NotNull
    @Size(min = 2, max = 100)
    private String address;
    @NotNull
    @Size(min = 11, max = 11)
    private String phone;
    @NotNull
    @Size(min = 2, max = 15)
    private String password;
    @NotNull
    @Size(min = 2, max = 15)
    private String rePassword;
}
@PostMapping(path = "/join")
public String join(@Valid UserJoinForm userJoinForm, BindingResult bindingResult){
    //에러발생
    if(bindingResult.hasErrors()){
        return "/login/join";
    }
   
    if(!userJoinForm.getPassword().equals(userJoinForm.getRePassword())){
        FieldError fieldError = new FieldError("userJoinForm", "rePassword", "암호가 일치하지 않습니다");
        bindingResult.addError(fieldError);
        return "login/join";
    }

    User userByEmail = userService.getUserByEmail(userJoinForm.getEmail());
    if(userByEmail != null){
        FieldError fieldError = new FieldError("userJoinForm", "email", "이미 존재하는 email입니다.");
        bindingResult.addError(fieldError);
        return "login/join";
    }

    User user = new User();
    BeanUtils.copyProperties(userJoinForm, user);

    PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
    user.setPassword(passwordEncoder.encode(user.getPassword()));

    UserRole userRole = new UserRole();
    userRole.setRoleName("USER");
    user.addUserRole(userRole);

    User saveUser = userService.addUser(user);

    return;
}
<form class="form" id="formJoin" role="form" autocomplete="off"  novalidate="" method="POST" th:object="${userJoinForm}" th:action="@{/users/join}" action="#">
    <div class="form-group">
        <label for="name">name</label>
        <input type="text" class="form-control form-control-lg rounded-0" name="name" id="name" required="" th:field="*{name}">
        <small class="text-danger" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></small>
    </div>
    <div class="form-group">
        <label for="email">email</label>
        <input type="text" class="form-control form-control-lg rounded-0" name="email" id="email" required="" th:field="*{email}">
        <small class="text-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></small>
    </div>
    <div class="form-group">
        <label>Password</label>
        <input type="password" class="form-control form-control-lg rounded-0" id="password" name="password" required="" th:field="*{password}" >
        <small class="text-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></small>
    </div>
    <div class="form-group">
        <label>Re Password</label>
        <input type="password" class="form-control form-control-lg rounded-0" id="rePassword" required="" th:field="*{rePassword}" >
        <small class="text-danger" th:if="${#fields.hasErrors('rePassword')}" th:errors="*{rePassword}"></small>
    </div>
    <div class="form-group">
        <label>phone</label>
        <input type="text" class="form-control form-control-lg rounded-0" id="phone" name="phone" required="" size="11" maxlength="11" th:field="*{phone}">
        <small class="text-danger" th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}"></small>
    </div>
    <div class="form-group">
        <label>address</label>
        <button type="button" class="btn btn-info btn-sm" id="addressSearch">search</button>
        <input type="text" class="form-control form-control-lg rounded-0" id="address" name="address" required="" th:field="*{address}">
        <small class="text-danger" th:if="${#fields.hasErrors('address')}" th:errors="*{address}"></small>
    </div>
    <div>
        <label class="custom-control custom-checkbox">
            <input type="checkbox" class="">
            <span class="custom-control-indicator"></span>
            <span class="custom-control-description small text-dark">Remember me on this computer</span>
        </label>
    </div>
    <i class="fab fa-facebook"></i>
    <div class="form-group row">
        <div class="col-sm-10">
            <a th:href="@{/sign-in/facebook}">facebook 가입 및 로그인</a>
        </div>
    </div>
    <button type="submit" class="btn btn-success btn-lg float-right" id="join">Join</button>
</form>
Share:

2018년 5월 8일 화요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 25일차(JPA, Valid)

  • 데이터베이스를 많이 사용하는 사이트는 어떤 곳일까?
    • 정적 데이터(사진사이트)
    • 비지니스 로직(은행)
    • DATA
  • 비지니스 로직이 많을때 여러개의 WAS를 사용
    • L4스위치를 통해 하나의 WAS를 통해 사용가능하게 할 수 있다.(비싸다)
    • 브라우저와 WAS사이에 nginx, 웹서버를 두어서 라운드 로빈방식으로 구현
      • 요청이 올때마다 하나의 WAS에 연결해준다.
      • 세션은 WAS남기 떄문에 문제가 있다. 그래서 세션정보를 공유하는부분을 만들어야한다.
      • Spring Session을 통해 Redis에 저장할 수 있다.
      • Nginx는 재시작을 해도 기존 사용자가 있다면 프로세스를 다 쓸때까지 종료되지 않는다. 새로 추가된 Nginx는 추가된 WAS에 연결되고 사용자가 사용하던 Nginx는 기존사용 종료후 재시작 되서 추가된 WAS와 연결된다. 무중단으로 서비스가 가능하다.
  • 값 검증
    • @Valid가 선언된객체에 설정을 바탕으로 검사후 BindingResult에 담아준다.
    • BindingResult.hasErrors : 에러가 있는지 검사한다.
    • 에러를 발생하려면 FieldError객체를 만들어서 BindingResult에 넣어준다.
    • dispatcherservlet이 메소드안에 선언된 객체를 만들어주고 값을 넣어준다.
    • 태그에서 object에 담긴 객체의 프로퍼티를 filed *{필드이름} 형식으로 사용한다
    • errors는 BindingResult에 있는 에러값을 출력해준다.
  • JPA
    • EntityManager가 제일 중요하다.
    • 영속성 상태일때만 작업이 일어난다.
    • 영속성을 부여하면 1차 캐시에 저장된다. 그리고 따로 스냅샷을(복사본) 만든다. 
    • 영속성 상태일때 @Id와 엔티티가 키가되고 엔티티가 값이된다.
    • ID를 자동생성으로 셋팅하면 DB에 insert가 먼저 일어나고 키를 얻어온다. 자동생성 하지 않으면 DB에 INSET하지 않고 ID를 가지고 1차캐시에 저장된다.
    • 1차캐시와 스냅샷이 서로 다를떄 UPDATE가 실행된다. 
    • EntityManager가 트랜잭션이 커밋될때 모아둔 SQL을 최적으로 실행한다.
    • EntityManager은 캐시에 데이터가 있으면 DB를 이용하지 않고 캐시에 있는것을 반환한다.
    • 비영속 : 객체만 생성한 단계
    • persist : 영속성 상태가 된다. insert가 작동하는것은 아니다. 조회를 할때도 영속성을 가진다.
    • detach : 비영속 상태가 된다.
    • 준영속상태 : 아이디를 가지고 있는 상태, 영속상태로 변경할 수있다.(merge)
    • @Transactional이 없으면 EntityManager를 관리하지 못한다.
    • 스프링 부트에서는 필터(OSIV) - DS - C - S - R - DB
      필터(OSIV) - DS - C 에서는 트랜잭션이 ReadOnly이다.
      S - R - DB 단계에서는 읽고 쓰기가 가능하다.
      레이지 로딩 사용하려고 필터단계에서 트랜잭션이 시작된다.
  • QueryDSL
    • JPQL을 오류나지않게 생성해주는 도구
  • JOOQ
    • natavie SQL을 사용하게 해주는 도구
  • 공부목록
    • Spring Session
Share:

2018년 5월 7일 월요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 24일차(Spring Security Filter,Spring Boot Resource)

  • Spring Security Filter
    • 필터에서 인증처리를 한다.
    • WebSecurity는 HttpSecurity를 통하지 않고 DispatcherServlet로 전송하고 싶을때 사용한다.
    • HttpSecurity는 인증처리를 실행한다.
    • addFilterBefore : 시큐리티 필터처리 되는 부분에서 중간에 필터를 넣는다.
    • 소셜 로그인
      • 소셜로그인 정보로 저장된 정보가 있는지 판단
        • 있다.
          • 인증정보 설정(Redirect)
        • 없다
          • 이미 이메일로 가입된 경우
            • 아이디, 패스워드로 로그인, 인증정보제거
          • 이메일이 없는 경우
            • 유저정보 저장, 유저커넥션 정보 저장, 인증정보 저장
          • 이미 로그인 상태
            • 유저커넥션정보 저장
  • Spring Boot 2.0
    • Spring Boot에서는 resources/static/ 에서 css, js디렉토리를 인식한다.
    • http://localhost:8080/css/a.css 로 접근이 가능하다
    • http://localhost:8080/static/css/a.css 로 접근가능하게 하려면 addResourceHandlers를 설정한다.
Share:

2018년 4월 27일 금요일

패스트 캠퍼스 JAVA 웹 프로그래밍 마스터 23일차(Spring Boot, HTTP, 배포 설정, Message Converter)

  • 로컬과 배포시 설정
    • 로컬에서는 메모리 DB를 사용하다가 배포시에는 DB설정을 변경 하여야한다.
    • Spring Profile(스프링 자체 기능)
      • 로컬, 개발, 운영 설정을 jar로 다 가지고 다닌다. 어디서든 한번에 환경에 맞게 배포를 할 수 있다.
      • 스프링 말고 다른언어가 있으면 에서는 배포에 대한 설정을 더 해야한다. 
    • 빌드도구 툴
      • 디렉토리별로 설정파일을 만든다. 로컬, 개발, 운영에 한번에 배포하려면 3번에 걸쳐 각각 해야한다.
    • VM option
      • 자바로 실행할때 옵션
      • java VM옵션 Helloworld 프로그램옵션
  • HTTP 데이터 통신
    • 브라우저에서 post로 값을 보낼때 header에 담아서 보낸다.
    • 브라우저에서 body에 JSON,xml등 다양한 형식으로 값을 담아서 보낼수 있다.
    • WEB API에서 DTO같은 객체에 담고 싶을때 Spring mvc 에서 spring message converter를 통해 처리할 수 있다. 요청이 올때와 응답할때 처리 해준다.
    • @RestController를 선언하면 body에 담아서 응답한다.
    • Form에서 submit하면 FormData형태로 값을 보낸다.
  • Spring Boot에서 배포관련 설정
    • Profile설정에 따라 다른 객체를 주입할 수 있다.
    • application.yml 파일에 다음과 같이 기본으로 사용할 profile을 설정할 수 있다.
    • spring:
        profiles:
          active: local
      
      
      my:
        address: 192.168.1.100  <----- 공통설정
      ---
      spring:
        profiles: local
      my:
        address: 127.0.0.1       <---- local
      ---
      spring:
        profiles: real
      my:
        address: 192.168.1.120   <---- real
      
    • 아래와 같이 @Value애노테이션을 이용하여 설정을 읽어왔다.
    • 
      @Controller
      @RequestMapping("/boards")
      public class BoardController {
      
          @Value("${my.address}")
          private String address;
      }
      
    • 실행시에 프로그램 아규먼트를 줌으로써 원하는 profile로 실행할 수 있다.
    • 
      java -jar 스프링부트애플리케이션.jar --spring.profiles.active=real
      
    • Profile설정에 따라 다른 객체를 주입할 수 있다.
    • 
      package examples.boot.simpleboard.service;
      
      public interface MyService {
          public String getName();
      }
      
      
      ckage examples.boot.simpleboard.service.impl;
      
      import examples.boot.simpleboard.service.MyService;
      import org.springframework.context.annotation.Profile;
      import org.springframework.stereotype.Service;
      
      @Service
      @Profile("real")
      public class RealServiceImpl implements MyService {
          @Override
          public String getName() {
              return "real";
          }
      }
      
      
      package examples.boot.simpleboard.service.impl;
      
      import examples.boot.simpleboard.service.MyService;
      import org.springframework.context.annotation.Profile;
      import org.springframework.stereotype.Service;
      
      @Service
      @Profile("local")
      public class MyServiceImpl implements MyService {
          @Override
          public String getName() {
              return "local";
          }
      }
      
      
      @Autowired
      MyService myService;
      
  • Spring boot 2.0
    • @EnableWebMvc 을 선언 안해줘도 된다. 선언 안해주면 기본으로 사용하는 메시지 컨버터를 사용한다.
    • @EnableWebMvc 을 선언하면 메시지 컨버터를 설정 해줘야한다.
    • spring boot는 의존성을 추가하면 자동으로 설정이 된다. 웹 프로그래밍과 관련된 내용들이 자동으로 설정된다. 기본적인 메시지 컨버터들도 자동으로 등록된다.
  • Hibernate
    • 엔티티를 선언하면 Hibernate가 해당 엔티티를 상속하여 프록시 객체를 만들어 오버라이딩 하여 사용한다.
  • Message Converter
    • spring 을 이용해서 메시지 컨버터를 등록하는 방법
      http://www.baeldung.com/spring-httpmessageconverter-rest
    • spring boot에서의 메시지 컨버터 설정
      • java config파일 중 WebMvcConfigurer를 구현하는 클래스에서 다음의 메소드를 오버라이딩한다.
      • converters 에 메시지를 컨버터를 등록하면 됩니다.
      • 
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters)
    • Spring Boot 2.0에서 hibernate 5를 사용하고 엔티티를 반환하면 Exception이 발생한다. 기존의 컨버터가 변환을 못한다.
      • 아래와 같이 컨버터를 생성한다
      • 변환하기 위해 jackson이 제공하는 하이버네이트 모듈을 추가한다. 
      • 이렇게 했을 경우 LocalDateTime이 jsr310형식으로 출력되지 않는다. 
      • jackson이 제공하는 jsr310모듈을 추가한다.
      • 
        // https://stackoverflow.com/questions/28862483/spring-and-jackson-how-to-disable-fail-on-empty-beans-through-responsebody
        // https://github.com/FasterXML/jackson-datatype-hibernate/issues/87
        // https://stackoverflow.com/questions/33727017/configure-jackson-to-omit-lazy-loading-attributes-in-spring-boot
        @Override
        public void configureMessageConverters(List> converters) {
        
         // http://jsonobject.tistory.com/235 iso8601 형식으로 날짜 정보 출력하기
         ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).modules(new JavaTimeModule()).build();
         mapper.registerModule(new Hibernate5Module());
        
         mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
         MappingJackson2HttpMessageConverter converter =
           new MappingJackson2HttpMessageConverter(mapper);
         converter.setPrettyPrint(true);
        
         converters.add(converter);
        }
        
  • ResponseEntity
    • HttpStatus 상태값 알맞게 사용하기
  • spring boot로 spring mvc 웹 프로그래밍을 하려면 다음의 라이브러리 의존성을 추가하면 됩니다.
  • <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  • Spring boot JPA 구성
    • Spring boot에서는 자동으로 필터가 추가되어있다.
    • 필터 - Controller - service -Repository
  • 크롬에서 네트워크 이력 남기기
    • 개발자 도구에서 Preserve log를 체크한다
  • 공부해보기
    • error handling
    • spring validation
    • osvi(JPA관련)
    • Scouter(성능 모니터링 툴)
    • Gatling 툴(네트워크 트래픽 공격)
    • OpenEntityManagerInViewFilter(OSIV)
Share: