[도서 리뷰] 프로그래머를 위한 기초 해석학



길벗 출판사의 8차 신간 서평 이벤트에 참여하였다. 본인은 리뷰 이벤트에 등록된 많은 신간 도서 중 <프로그래머를 위한 기초 해석학> 책을 신청하였는데, 그 이유는 최근 자연어 처리 학습을 하며 수학에 대한 전반적인 기초가 많이 부족하다고 생각했기 때문이다. 그리고 단지 '기초'라는 단어가 붙어 있었다는 이유로 신청을 한 이 결정은 큰 후회를 낳게 되었다..

리뷰

1장에는 집합과 사상 그리고 실수의 특성 등 기본적으로 수학책의 초반부에 항상 등장하는 주제가 다루어 졌다. 책의 설명이 전반적으로 그림을 이용하여 진행되다 보니, 기본적 지식을 재점검하는데 큰 도움이 되었다. 그리고 이제 해석학의 본질적 목표인 수학적 개념들의 증명에 대한 내용들이 이어진다.

이 책은 초반부부터 책의 독자를 대학교 1, 2학년 수준 이상의 대학 수학을 이해하고 있는 사람으로 산정하고 있다. 따라서 문과 출신으로 고등학교 수학부터 이미 이과인들과의 간격이 벌어진 나에게는 책의 뒤로 갈수록 한 장 한 장을 넘기는게 쉽지 않은 책이었다. 그럼에도 그림을 이용한 추가적 설명들이 나를 책을 포기하는 행동으로부터 지켜주었다.

특히 유익하게 본 챕터는 6장 다변수함수 부분이었다. 딥러닝을 학습할 때, '역전파'라는 개념이 나온다. 한 입력값이 최종 출력값에 얼마만큼의 영향을 주는지를 계산하기 위해 출력값으로부터 입력값의 영향을 역으로 추적해나가는 이 행위는, 편미분 개념의 이해를 필요로 한다. 이전에는 굉장히 high-level 수준에서 '아, 이러한 개념으로 진행되는구나'라고 생각했던 이러한 편미분의 개념을 증명을 통해서 이해하려고 노력하니 어떠한 개념이구나라는 것을 전보다 깊게 이해할 수 있게 되었던 것 같다.

사실, 리뷰를 위해 책을 꾸역꾸역 모두 넘겨서 보려고 했지만 이해를 깊게 하거나 어떤 깊은 통찰력을 얻기에는 내가 이미 가지고 있는 수학적 지식이 부족해 쉽지 않았던 것 같다. 그럼에도 나같은 문과인들도 최소한 '시도'라도 해 볼 수 있을만한 기초 해석학 서적이 있다는 것은 단비와도 같다고 느꼈다. 연습문제도 거의 건드려 보지도 못할 만큼 어려운 것이 '증명'의 세계였지만, 마냥 어렵다고 멀리하지 않을 수 있을만큼 친절하게 도와주는 서적이었다는 것에 큰 의의가 있다고 생각한다. 인공지능에 관심 있고, 수학을 어려워 하셨던 문과 출신 분들께는 나와 같은 경험을 할 수 있는 좋은 책이라고 생각한다.

<프로그래머를 위한 기초 해석학>의 자세한 내용은 한빛미디에 홈페이지에서 확인하실 수 있습니다.


HTTP 상태 코드

  • 상태 코드는 서버로부터 리퀘스트 결과를 전달
  • 200 OK와 같이 3자리 숫자와 설명으로 나타남
    • 첫 째 자리는 리스폰스의 클래스를 의미
    • 나머지 2자리는 딱히 분류가 없음

2xx 성공

2xx 리스폰스는 리퀘스트가 정상으로 처리되었음을 나타냄

  • 200 OK: 클라이언트가 보낸 리퀘스트를 서버가 정상적으로 처리하였음
    • 상태 코드와 함께 되돌아 오는 정보는 메소드에 따라 다름
    • GET의 경우, 리퀘스트 리소스에 대응하는 엔티티
    • HEAD의 경우, 리퀘스트 리소스에 대응하는 헤더 필드
  • 204 No Content: 리퀘스트는 성공했지만 리소스는 돌려주지 않음
  • 206 Partial Content: Range에 의해 범위가 지정된 리퀘스트에 의해 서버가 부분적 GET 리퀘스트를 받음

3xx 리다이렉트

3xx 리스폰스는 리퀘스트가 정상적으로 처리를 종료하기 위해 브라우저 측에서 특별한 처리를 수행해야 함을 의미

  • 301 Moved Permanently: 리퀘스트된 리소스에 새로운 URI 부여되어, 이후로는 바뀐 URI를 사용해야 한다고 알림
    • 따라서 북마크가 설정되어 있는 경우, URI 변경을 할 것을 권함
  • 302 Found: 리소스 URI가 일시적으로 다른 곳에 이동되어 있음을 알림
    • 영구적으로 이동한 것은 아니기 때문에 북마크 변경을 권하지 않음
  • 303 See Other: 리소스 URI가 다른 곳에 있다는 것을 알림 + GET 메소드의 사용을 권함
    • 302 Found와 같은 기능이지만, GET 메소드의 사용을 권하기 때문에 303을 사용하는 것이 바람직함
    • 301, 302, 303 리스폰스 코드 수신 시 브라우저는 POST를 GET으로 바꾸어 리퀘스트를 자동으로 재송신
      • 301, 302에서는 POST를 GET으로 바꾸는 것을 금지하지만, 실상에서는 변경하는 것이 대부분
  • 304 Not Modified: 클라이언트가 조건부 리퀘스트를 했을 때 리소스에 대한 접근은 허락하지만, 조건이 만족되지 않았음을 알림
  • 307 Temporary Redirect: 302 Found와 같은 기능 수행

4xx 클라이언트 에러

4xx 리스폰스는 클라이언트의 원인으로 에러가 발생했음을 나타냄

  • 400 Bad Request: 리퀘스트 구문이 잘못되었음을 나타냄
    • 브라우저는 해당 상태 코드를 200 OK와 같이 취급
  • 401 Unauthorized: 리퀘스트에 HTTP 인증 정보가 필요하다는 것을 알림
    • 브라우저에서 처음 401 리스폰스를 받은 경우에는 인증을 위한 다이얼로그가 표시됨
  • 403 Forbidden: 리퀘스트된 리소스의 액세스가 거부되었음을 알림
    • 이유를 명확히 하는 경우에는 Entity Body에 기재해서 유저 측에 표시
  • 404 Not Found: 리퀘스트한 리소스가 서버 상에 없다는 것을 나타냄

5xx 서버 에러

5xx 리스폰스는 서버 원인으로 에러가 발생하고 있음을 나타냄

  • 500 Internal Server Error: 서버에서 리퀘스트를 처리하는 도중에 에러가 발생했음을 알림
  • 503 Service Unavaliable: 일시적으로 서버가 과부하 상태이거나 점검중이기 때문에 리퀘스트 처리가 불가함을 알림
    • 상태가 해소되기까지 걸리는 시간을 Retry-After 헤더 필드에 전달하는 것이 좋음

'Software Convergence > Network' 카테고리의 다른 글

Network Layer (2)  (0) 2018.10.08
Network Layer (1)  (0) 2018.10.07
Transport Layer  (0) 2018.10.07
OSI 7 Layers Overview  (0) 2018.10.01
Application Layer  (0) 2018.09.30

클래스 Object와 Wrapper 클래스

Object class

  • 클래스 Object는 Java에서 모든 클래스의 superclass
  • Java의 모든 클래스는 이미 equals와 toString 메서드 등을 가지고 있음
  • class Object의 member method
    • equlas, toString, getClass, hashCode()
  • toString method는 사용자가 원하는 결과를 출력하도록 Override를 해주어야 함
  • Object 클래스의 equals 메서드의 매개변수는 Object 타입
    • 매개변수로 제공된 객체와 자기 자신의 동일성을 검사
      • 값을 비교하는 것이 아님
    • 따라서 원하는 값의 비교를 해주기 위해서는 역시 Override 해주어야 함

Wrapper class

  • Java에서 primitive type 데이터와 non-primitive type 데이터(객체)는 근본적으로 다르게 처리됨
  • 때로 primitive type 데이터를 객체로 만들어야 할 경우가 있음
    • 이럴 때, Integer, Double, Character 등의 Wrapper class를 이용
int a = 20;
Integer age = new Integer(a);
int b = age.intValue();  // b becomes 20

// 데이터 간 형변환
String str = "1234";
int d = Integer.parseInt(str);  // d becomes 1234
  • Autoboxing과 Unboxing
Object[] theArray = new Object[100];
theArray[0] = 10; // 컴파일러가 자동으로 10을 Integer 객체로 변환해줌(autoboxing)
int a = (Integer)theArray[0]; // theArray[0]에 저장된 걱은 Integer 객체이지만 컴파일러가 자동으로 정수로 변환(unboxing)


'Software Convergence > Java' 카테고리의 다른 글

다형성: Polymorphism  (0) 2018.11.05
static과 접근제한자  (0) 2018.10.19
상속의 단점과 Strategy Pattern  (0) 2018.09.03
Generic 자료형 실습  (0) 2018.07.12
instanceof / encapsulation  (0) 2018.07.04

스프링 부트 원리 (2)

자동 설정 구현

  • 보통 configure와 starter는 패키지를 나누어서 만듦
    • xxx-spring-boot-autoconfigure 모듈: 자동 설정
    • xxx-spring-boot-starter 모듈: 필요 의존성 정의
  • 그러나, 그냥 하나로 만들고자 할 때는 xxx-spring-boot-starter에 함께 집어 넣음
  • 구현 방법
  1. 의존성 추가
<dependencies>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
  </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure-processor</artifactId>
      <optional>true</optional>
  </dependency>
</dependencies>
     
<dependencyManagement>
  <dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-dependencies</artifactId>
          <version>2.0.3.RELEASE</version>
          <type>pom</type>
          <scope>import</scope>
      </dependency>
  </dependencies>
</dependencyManagement>
  1. @Configuration 파일 작성
  2. src/main/resource/META-INF 폴더에 spring.factories 파일 만들기
  3. spring.factories 안에 자동 설정 파일 추가
    #groupID + Configuration 파일명
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.devfon.HolomanConfiguration
  1. mvn install
  • 다른 maven project에서도 사용이 가능해지도록 local maven 저장소에 jar 파일 설치하게 됨
  • 다른 프로젝트에서는 아래와 같이 다른 프로젝트에서 의존성 추가하여 사용
<dependency>
    <groupId>com.devfon</groupId>
    <artifactId>devfon-spring-boot-starter</artifactId>
    <version>1.0-SNAP</version>
</dependency>
  • 아래와 같이 의존성이 추가된 것을 확인할 수 있음

  • but, 한 가지 문제가 발생!
    • Component Scan으로 새로운 Bean을 등록한다 하더라도, 이후에 AutoConfiguration의 Bean을 다시 등록하기 때문에 AutoConfiguration의 Bean이 이전 Bean을 무조건 덮어쓰는 문제
    • 이를 해결하기 위해서는 @ComponentScan으로 읽힌 Bean들이 항상 우선 시 되어야 함
    • 해결 방법: @ConditionalOnMissingBean
      • AutoConfiguration에서 설정한 Bean에 @ConditionalOnMissingBean 애노테이션을 달아줌
  • 위 해결 방안을 통해 기정의된 convention이 아닌 내가 정의한 Bean의 사용이 가능해짐! -> "Customizing"
  • 더 나아가...
    • 다른 프로젝트에서 해당 Bean을 사용할 때, 매 번 Bean을 등록해주기가 번거롭다.
    • 다른 편리한 방법이 없을까?
    • ConfigurationProperties의 사용!
  1. jar 파일로 만들고자 하는 프로젝트에 ConfigurationProperties 파일 추가
@ConfigurationProperties("holoman")
public class HolomanProperties {

    private String name;
    private int howLong;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHowLong() {
        return howLong;
    }

    public void setHowLong(int howLong) {
        this.howLong = howLong;
    }
}
  1. Configuration 파일에 EnableConfigurationProperties 옵션 추가
@Configuration
@EnableConfigurationProperties(HolomanProperties.class)
public class HolomanConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public Holoman holoman(HolomanProperties properties){
        Holoman holoman = new Holoman();
        // properties 파일로부터 값을 읽어와 생성하는 방식!
        holoman.setHowLong(properties.getHowLong());
        holoman.setName(properties.getName());

        return holoman;
    }
}
  1. jar 파일 사용하는 프로젝트의 application.properties에서 설정값 적용
holoman.name = sang
holoman.howLong = 365


[도서 리뷰] 스프링 부트 입문자를 위한 <처음 배우는 스프링 부트 2>



한빛미디어의 서평 이벤트로 <처음 배우는 스프링 부트 2> 책을 받아 읽어 보았다. 정말 초심자의 입장인 나의 상태로 책을 읽어 보았기에, 알맞은 눈높이에서 리뷰를 진행할 수 있을 것 같다는 생각으로 리뷰를 남겨본다.

1장

스프링 부트에 대한 기본적 소개와 간단한 특징들을 다루고 있다. 스프링 부트가 스프링에 비해 간결한 설정 방법을 가질 수 있는 이유는 무엇인지, 책을 관통하는 프로젝트 '커뮤니티 게시판'의 설계를 위해서는 어떠한 설계도가 필요한지에 대한 이야기가 주를 이룬다. 이후, starter가 포함하고 있는 의존 관계에 대한 짧은 설명이 등장하는데 사실 이 부분은 스프링 부트 혹은 스프링을 전혀 접해보지 못했다면 이해할 수 없는 부분이었던 것 같다. 하지만, 책을 두 번 째 보면서 그 중요성을 다시 느낄 수 있는 부분이었다.

2장

스프링 부트 기반의 프로젝트를 생성하기 위해 필요한 환경 설정에 대한 내용을 다룬다. 즉, 이번 장에서는 JDK, IntelliJ 그리고 빌드 툴인 Gradle의 활용법 등이 상세 내용으로 등장한다. JDK와 IntelliJ의 경우 스프링 부트 교재를 본다는 가정 하에 이 책을 보는 모든 독자가 아는 부분일 것이기 때문에 이 부분들을 줄이고, Gradle의 특징이나 활용법에 대해 더 다루었으면 좋았을 것 같다. 아무래도 maven 기반의 프로젝트를 실습해와본 독자들이 많았을 것이고, 나 같은 경우도 gradle 기반의 프로젝트를 처음 따라 배워보았다. 따라서 막히는 부분에 있어 검색할 때 정보가 많이 나오지 않아 답답한 부분이 종종 있었다. gradle에 대한 설명이 조금 더 추가되어 있었더라면 이러한 가려운 부분을 조금이나마 해결해줄 수 있었을 것 같다! 이후 중요한 애노테이션 설정들에 대한 설명이 들어간 것들은 아주 만족스러웠다.

3장

3장은 스프링 부트 테스트를 다루고 있다. 테스트 스타터로 뭉쳐진 의존성을 통해 스프링 부트가 얼마나 쉽게 테스트의 수행이 가능할 수 있는지 실습을 통해 보여주고 있는 장이다. TDD는 개발을 함에 있어 매우 중요한 덕목(?)으로 여겨지기 때문에 책에 등장해야 할만한 적합한 주제이긴 하나, 갑작스러운 등장이 약간 당황스러웠던 장이었던 것 같다. 3, 4장의 위치가 바뀌고 4장에서 생성하는 모듈들에 대한 단위 테스트를 실습하는 것을 이후에 보여주었다면 더 나은 구성이었을 것 같다는 생각을 해본다.

4장

가장 도움을 많이 받았던 스프링 부트 웹에 대한 설명을 다루고 있는 4장이다. 기본적인 게시판의 구현을 실습하며, 어떠한 전체 구조를 지녀야 웹 페이지를 설계할 수 있는지에 대한 지식을 얻을 수 있었던 장이었다. 또한 게시판 구현에서 어려움을 많이 발생시키는 페이징 처리나, 서버 사이드 템플릿 '타임리프'에 대한 정보를 다루고 있었던 것도 아주 만족스러웠다. 아마 나 같은 초심자에게는 이번 4장과 6장이 책에서 얻을 수 있는 가장 좋은 정보를 담고 있는 장이라고 생각한다.

5장

스프링 시큐리티와 oAuth에 대한 내용을 담고 있는 5장이다. 이 장 역시 알짜배기 장이었던 것 같다. 스프링 시큐리티가 사실 초보가 처음 접하기엔 어려운 부분이라고 생각하는데 좋은 설명과 실습으로 개념을 잘 진행할 수 있도록 구성이 잘 짜여져 있었던 장이다. 그리고 요즘 핫한 기술인 oAuth를 다루고 있다는 점에서 좋은 점수를 주고 싶다. 각종 sns와 어떻게 연동을 할 수 있는지를 친절한 설명을 통해 도와주고 있다.

6장

4장과 함께 굉장히 마음에 들었던 6장이다. 6장은 REST API 서버를 다루고 있다. 최근 Rest API에 대한 관심이 굉장하다고 생각한다. 내가 최근에 접해서 그 인기를 늦게 체감한 것일 수도.. 각설하고, 이 장이 특히 좋았던 이유는 Rest API의 구성을 MVC 패턴으로도 한 번, 스프링 데이터 레스트로도 한 번 설계해보며 그 설명의 끈을 이어나간다는 점이다. 저자의 섬세함이 돋보인 장이라고 생각한다. 초기 설정을 위해 DB로 사용하고 있는 MySQL에 대한 설명이 조금 들어있었다면 좋았을 것 같기는 하지만, 이 정도는 독자가 삽질하며 배워봐도 좋은 부분이라고 생각한다. 실제로 나는 mysql connector 설정 때문에 삽질을 많이 했다.. MySQL이 8 버전 이후로 넘어가면서 많은 default 설정들이 바뀌었던 것 같다. 그럼에도 삽질을 통해 웹 서버와 API 서버를 연동했을 때, 이 장의 빛을 다시 느낄 수 있었다!

7장

마지막 장은 스프링 부트 데이터 배치를 다루고 있다. 사실 이 장은 리뷰를 하기 민망할 정도로 주의 깊게 보지 못했다. 아직 어렵다.. 이후에 다시 실습해보면 리뷰를 할 수 있을 것 같다! 다만 평이 좋은 것을 보니 좋은 내용을 다루고 있는 것 같다.


총평

리뷰 이벤트를 통해 증정 받은 도서이긴 하지만 주위에 누군가 스프링 부트를 배워보고 싶다고 하면 주저없이 추천을 해주고 싶은 책이다. 책이 짧은게 아쉬울 정도로 알짜배기 내용들을 잘 녹아내고 있는 책이라고 생각을 한다. 아마, 조금의 분량이 더 주어졌다면 분명 더 좋은 퀄리티의 책이 되었을 것 같다. 저자 분이 처음 집필한 책이라고 들었는데 아마 이후에 더 좋은 책의 저자가 되실 것 같다. 초심자의 시선으로 유익하게 볼 수 있었다 :)

<처음 배우는 스프링 부트 2>의 자세한 내용은 한빛미디에 홈페이지에서 확인하실 수 있습니다.


스프링 부트 원리 (1)

의존성 관리 이해

  • spring-boot-dependencies -> spring-boot-starter-parent -> 내 프로젝트의 pom 순으로 의존성 계층 구조가 성립
  • spring-boot-dependencies의 dependencyManagement에 각종 의존성들에 대한 version이 모두 명시되어 있음
  • 따라서 내 프로젝트의 pom에 spring-boot-dependencies에 정의되어 있는 의존성을 사용하게 될 경우, 직접 version을 명시해주지 않더라도 version 정보를 가져오게 됨
  • 즉, 각각의 starter에 이미 의존성들이 제공되고 있기 때문에 우리가 직접 관리해야 할 의존성의 수가 줄어들게 됨
    • 우리가 관리해야 하는 일이 줄어든다는 의미
    • version up을 하고자 할 때, third-party library의 어느 version과 호환되는지 모르는 문제도 해결 가능
    • 깔끔한 의존성 관리
  • pom에서 지원하지 않는 의존성을 추가하고자 할 때는 직접 version을 명시해주어 함!
  • 특정 library의 특정 version의 사용을 원하는 경우 명시해주어 사용 가능 -> 편리한 Customizing
  • 자기만의 의존 구조를 성립시키고 싶을 때는? (두 가지 방법)
    1. 직접 parent pom을 만들어 현재 프로젝트의 parent로 만들고, 그 parent로 spring-boot-starter-parent를 두는 것 (추천)
      • 즉, spring-boot-starter-parent -> 내 프로젝트의 parent -> 내 프로젝트의 구조를 만드는 것
    2. 현재 프로젝트의 parent를 만들고, 해당 parent에 섹션을 만들어 spring-boot-starter를 추가하여 사용 (비추천)
      • but, spring-boot-starter에는 dependency들만 가져오는 것이 아니라, 그 밖의 다른 설정들이 존재
        • ex) java 1.8, UTF-8, 각종 yml, plugin 설정 등
      • 이 설정들을 가져오지 못하는 불편함 존재

의존성 관리 응용

    1. 버전 관리 해주는 의존성 추가
    • spring-boot-starter-data-jpa
    1. 버전 관리 안해주는 의존성 추가
    • ModelMapper: version을 명시해주어야 함
<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>2.3.1</version>
</dependency>
  • What is ModelMapper?
# Source model
class Order {
    Customer customer;
    Address billingAddress;
}

class Customer {
    Name name;
}

class Name {
    String firstName;
    String lastName;
}

class Address {
    String street;
    String city;
}

# Destination model
class OrderDTO {
    String customerFirstName;
    String customerLastName;
    String billingStreet;
    String billingCity;
}

# Domain to DTO를 자동으로 해주는 기능!
ModelMapper modelMapper = new ModelMapper();
OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class)
    1. 기존 의존성 버전 변경하기
    • 내 프로젝트 pom의 에 직접 명시해주어 변경 가능!
    <properties>
        <spring.version>5.0.6.RELEASE</spring.version>
        <java.version>1.9</java.version>
    </properties>

자동 설정 이해

  • @Configuration?
    • Bean을 등록하는 java 설정 파일
  • SpringBootApplication의 핵심 annotation들!
    • @SpringBootConfiguration
    • @ComponentScan
    • @EnableAutoConfiguration
  • 스프링 부트는 Bean을 두 번 등록
    1. ComponentScan으로 한 번
    2. EnableAutoConfiguration으로 또 한 번
  • 1단계: @ComponentScan
    • @Component을 달고 있는 class들을 Scan해서 Bean으로 등록
    • @Configuration, @Repository, @Service, @Controller, @RestController 들도!
  • 2단계: @EnableAutoConfiguration
    • springframework.boot.autoconfigure.EnableAutoConfigure 내 META-INF의 spring.factories 파일 읽어옴
    • 그 아래의 @Configuration들 읽어옴
    • but, @ConditionalOn'XxxYyyZzz'
      • 즉, 조건에 따라 Bean으로 등록하기도 하고, 안하기도 함!

스프링 부트 시작하기

스프링 부트 소개

  • 토이 수준이 아닌 Spring 기반의 Production-grade의 독립적 어플리케이션을 빠르고 쉽게 만들 수 있도록 도와줌
  • Opinionated view: 개발자가 직접 설정하지 않고도, Spring boot가 정의하는 Convention을 자동으로 설정해주는 것
    • 따라서 모든 것을 일일이 설정하지 않아도 됨
    • Spring platform 뿐 아니라, third-party library에 대한 설정도 자동으로 해줌
      • ex) Tomcat의 8080 port 설정...
  • 스프링 부트의 목표
    • 모든 스프링 개발에 있어 빠르고, 폭 넓은 사용성을 제공
    • Opinionated view를 제공하지만, 개발자가 원할 시 쉽고 빠르게 설정 변경 가능
    • 비즈니스 로직을 구현하는데 필요한 기능 뿐 아니라 Non-functional한 기능도 제공
    • xml 설정을 더 이상 하지 않고, code generation을 하지 않음
      • 쉽고 명확하며, 더 편리하게 customizing이 가능해짐!

스프링 부트 시작하기

  • group id는 보통 package명으로 설정
  • spring boot의 parent로 spring-boot-starter-parent 추가해줌
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>
  • Maven 간 프로젝트 계층 구조 만드는 것

    • spring-boot-starter-parent를 부모 프로젝트로 설정
    • Spring boot가 제공하는 의존성 관리와 매우 관련이 깊은 설정!
  • spring-boot-starter-web 의존성 추가

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
  • build plugin 추가
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  • IntelliJ에서 psvm 으로 Main 함수 추가 가능
  • mvn package로 maven project build 가능
    • jar 파일을 결과물로 생성!
  • 이후, java -jar 'jar 명'으로 jar 파일 생성 가능

스프링 부트 프로젝트 구조

  • 메이븐 기본 프로젝트 구조와 동일
    • 소스 코드
    • 소스 리소스
      • classpath가 root dir 의미
    • 테스트 코드
    • 테스트 리소스
  • @SpringbootApplication이라는 annotation이 달려 있는 MainApplication class는 가장 최상위 패키지에 두는 것이 추천됨
    • @SpringbootApplication을 기점으로 Component scan을 하기 때문
    • java 폴더 바로 밑에 Main class 두게 될 경우, 프로그램에 존재하는 모든 패키지를 뒤져서 component를 찾는 비효율 발생
    • 따라서, 다른 패키지에 있는 moduleㅡㄹ은 component로 등록이 안되기 때문에 디렉토리 구조를 설정는데 주의!

다형성: Polymorphism

  • Super class 타입의 변수가 Sub class 타입의 객체를 참조할 수 있다!
  • 예시) Notebook class가 Computer class를 상속 받고 있다고 가정
Computer computer = new Notebook("Bravo", "Intel", 4, 240, 2/4, 15)
  • Computer 타입의 변수 computer가 Notebook 타입의 객체를 참조하고 있음!

  • 다음의 예를 생각해보자
  • 가정) Computer와 Notebook class는 모두 toString method를 지니고 있음
System.out.println(compter.toString());
  • 위 예에서의 toString은 두 class 중 어느 class의 method를 호출하게 될까?
  • 답은 Notebook class의 toString !
    • 즉, 동적 바인딩(dynamic binding)이 일어남

      • Runtime 중에 참조 객체를 결정
    • cf. Computer class의 toString을 호출하는 경우, 정적 바인딩
      • 컴파일러가 참조 객체를 결정


'Software Convergence > Java' 카테고리의 다른 글

Object 클래스와 Wrapper 클래스  (0) 2018.11.13
static과 접근제한자  (0) 2018.10.19
상속의 단점과 Strategy Pattern  (0) 2018.09.03
Generic 자료형 실습  (0) 2018.07.12
instanceof / encapsulation  (0) 2018.07.04

Spring boot camp #3

기본적인 웹 Template

  • application과 사용자의 접점인 boundary
    • 조회 화면, 글쓰기 화면 등이 boundary의 예
  • 사용자가 요청한 업무를 처리하는 control
  • 게시물과 같은 entity
  • 외부 시스템(ex. 외부 결제 시스템)과의 접점도 필요함
    • 이 역시 boundary로 구현하여 control과 연동시킴
  • Transaction은 Spring JDBC, Spring Data JPA 그리고 외부 웹에서 자료를 받아오기 위한 Rest template 등에 의해 수행
    • DBMS 관리와 SQL에 대한 지식을 DBA 만큼은 아니더라도 갖추어야 함

Java와 Database programming

  • 입력, 수정, 삭제
      1. DB connection: connection Interface, DriverManager class 사용
      1. SQL 준비(ex. insert into table명 values(...))
      • DB가 sql을 이해하기 위해 문장을 parsing 하는데, 이는 큰 overhead를 발생시킴
      • parsing 결과를 DB 자체에 저장해두었다가, 완전히 동일한 sql문이 오면 이전에 저장해둔 값 사용
        • insert의 경우, values가 계속 변화할 수 있기 때문에 '?'로 채워뒀다가 ? 값만 바꾸어 사용 -> 성능 개선
      1. 바인딩: ?에 값을 채워주는 것
      • PreparedStatement interface 사용
      1. PreparedStatement의 executeUpdate method 호출
      1. DB connection close
  • 데이터 조회
      1. DB connection
      1. SQL 준비
      1. 바인딩
      1. executeQuery() method 실행
      1. ResultSet Interface의 next() method 이용하여 row 한 건 씩 읽어옴
      • 즉, SQL의 select 문에는 ResultSet interface가 사용됨
      1. 읽어온 row를 column별로 잘라서 사용
      • ResultSet의 getXXX method 사용
      • ex) getName(), getTitle()...
      1. DB connection close
  • connection Interface는 DBMS 프로그램별(MySQL, Oracle..)로 다름
    • 따라서, 이 Interface를 구현하는 class는 해당 DBMS 프로그램에 존재
    • 이 class를 Driver class라 함
  • DBMS를 다루다 보면 기능별로 중복된 코드가 발생할 수 있음
    • 그리고 이는 지루한 프로그래밍을 유발
    • 또한 Java 코드로 관리하기가 어려워짐
      • 문자열이 단순히 + 연산으로 이어지기 때문!
    • xml 파일에 id값 부여하여, SQL 코드 따로 관리하자!
  • 객체에 있는 정보를 ?에 넣어주고, data를 조회하면 객체로 받아올 수는 없을까?
    • SQL 중심으로 database 코드를 짜는 SQL mapper
      • MyBatis, Spring JDBC 등이 그 예
    • SQL만 작성하면 되기 때문에 더 쉽게 프로그래밍이 가능해짐
  • 그러나 SQL은 객체지향적이지 않음
    • ex) 게시물과 댓글의 1:n 관계
      • 게시물의 getComments()와 같은 method로 댓글을 읽어오고 싶음
      • 두 Entity 간의 관계를 객체지향적으로 표현하기가 어려움
        • 패러다임의 충돌: SQL적 사고와 객체지향적 사고에는 근본적인 차이가 존재
  • DBMS 프로그래밍을 객체지향적으로 할 수 있지 않을까?
    • 그러려면 우선 SQL 자체를 제거해야 한다!
    • 개발자는 객체만 생각하고, SQL은 어디선가 자동으로 만들어주도록 하자
    • 이렇게 나온 기술이 ORM(Object Relation Mapping) -> Hibernate
      • Hibernate는 어렵지만, 객체지향적 사고를 좋아하는 사람들에게 환영 받는 기술
      • Java 측에서 JPA(Java의 ORM 표준)이 등장!
      • JPA를 사용하면 SQL을 모르더라도 DBMS 사용 가능
  • Spring은 또 다른 고민에 빠짐
    • Data 저장소는 DBMS만 존재하는 것이 아님
      • ex) NoSQL(like. MongoDB), Hadoop...
    • 한 단계 더 추상화하여, Spring Data라는 module 제공
      • Spring Data JPA를 사용하여, module이 JPA 관리하도록 함
      • 때문에 Spring Data MongoDB와 같은 module도 존재
  • 결과적으로 JDBC -> Hibernate & JPA -> Spring Data의 발전 구조
    • 수업에서는 이를 다 다룰 수는 없기 때문에 Spring Data 위주로 다룸

about JPA

  • Spring Data JPA를 추가하면?
    • DataSource) (Interface: Connection Pool)
      • Spring Boot 2 에서는 HikariCP 사용
    • PlatformTransactionManager (transaction 관리자)
      • Spring은 AOP로 트랜잭션 관리 지원
    • JDBC Driver
      • h2: 인메모리 DBMS
      • web 환경 같이 사용할 경우에는 localhost:8080/h2-console로 클라이언트 접속이 가능
  • 아래와 같은 starter와 jdbc 드라이버 의존성을 추가하면, 자동으로 DataSource와 PlatformTransactionManager에 대한 설정이 추가 됨
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
  • DataSource ---> Connection Pool ---> DBMS
    • Connection Pool은 DBMS와 미리 여러 개의 connection을 하고 있음
    • 즉, DataSource를 사용한다는 것은 Connection을 DataSource에게 요청하는 것
  • DataSource에게서 받은 Connection의 close() method는 Connection을 끊는다는 것이 아니라, CP에 되돌려주는 것!
    • Connection은 받아서 빨리 돌려주어야 함
    • 그러기 위해서는 SQL이 빠르게 동작해야 함
    • Slow query를 해결하여 성능 향상을 해주어야 하는 이유
  • spring.datasource.initialization-mode= always 설정은 sample data 생성하기 때문에 test 용이하게 할 수 있음
  • spring.jpa.hibernate.ddl-auto= create-drop 설정은 table이 존재하지 않을 시, 자동으로 hibernate가 table 만들도록 하는 설정. 존재한다면 삭제 후 재생성

  • JPA에서의 '저장'은 영속적이다?
    • ex) 회원이 탈퇴할 때 까지 회원 정보는 유지되어야 함
    • entity 객체를 영속적 정보를 저장해주어야 함
  • 영속 상태가 되기 위해서는 반드시 식별자 값을 가지고 있어야 함
  • insert 뿐 아니라 조회할 때도 해당 데이터는 영속적이어짐
  • EntityManager가 영속성 관리
    • 때문에 EntityManager는 Bean으로 등록되어 Spring이 관리해주도록 해야 함
  • EntityManager는 transaction 단위로 작업을 수행
    • transaction: 하나의 논리적 작업 단위
    • commit이나 rollback이 하나의 transaction을 마치게 함
  • 이처럼 Transaction 단위로 동작하는 것을 Business method라 함
    • 그리고 이를 가지고 있는 객체가 Service 객체
    • 따라서 객체 설계 시에는 해당 객체의 Business method가 무엇이 있을지에 대한 고민이 필요
  • Service 객체는 CRUD를 위해 Repository(DAO) 참조
  • Service 객체의 method에 @Transactional 혹은 @Transactional(readOnly = true) annotation 있으면 proxy 생성되어, Transaction 알아서 관리
    • PlatformTransactionManager가 이 작업을 수행해줌
      • 이는 Connection에 의존하는 관계
      • Connection이 transaction begin, commit, rollback 등을 가지고 있기 때문
    • readOnly 인자는 조회(select)만 하는 method일 경우 사용
      • 조회할 때 매 번 commit을 수행하면 성능이 확 떨어지기 때문!
    • proxy는 try문 내에서 super.'method' 수행 후, commit
    • RuntimeException는 catch 해서 rollback 처리
  • 같은 transaction 안에서는 1차 캐시가 적용됨
    • 이전에 불러온 data를 또 요청하면 다시 가져오는 것이 아니라 이미 가지고 있는 것을 돌려줌
    • 즉, 같은 id에 대한 query는 한 번만 수행되는 것
  • 영속성이 부여되면 snapshot으로 복사본이 하나 만들어짐
    • Entity의 수정 기능은 setter로 행하게 됨
    • 기존에 만들어진 snapshot과 setter로 수정된 entity의 값을 비교해서 다르면 자동으로 update 문장 만들어 update 반영
  • Entity들 간의 관계에서는 방향이 중요함
    • ex) 부서(1) <----> 사원(N) 일 때 부서는 List<사원>, 사원은 부서라는 field를 가지고 있어야 함
    • 이럴 때 List<사원>에는 @OneToMany라는 annotation을, 부서에는 @ManyToOne annotation이 붙음
      • @OneToMany에는 (mappedBy='department')와 같은 것을 추가해주어 어떤 field로 연동되는 것인지 알려줌
      • Foreign Key를 위해 사원에는 @JoinColumn(부서_id)와 같은 annotation을 추가
    • ex) Member(N) <----> MemberRole(N)
    • 실제 다대다 관계는 일대다-다대일로 풀려짐
    • 다대다 관계에는 @JoinTable annotation이 붙어야 함
  • 부서.get사원들() 하면 SQL 실행 안됨 -> 단순히 Persist 속성만 넘어옴
    • 실제로 List를 사용할 때 select 문이 실행됨 -> Lazy Loading
  • Entity를 만들면 hibernate가 해당 entity에 대한 proxy를 생성
    • 실제로 사용되는 것은 우리가 만든 entity가 아닌 해당 proxy
  • 1 + N query 문제
    • 목록 가져오는 query 1번
    • Lazy Loading으로 정보 가져오는 N번 -> 너무 많은 query가 수행되어야 한다!
    • Repository에 method를 추가해서 해결
      • 부서와 사원을 fetch join: 관련된 부서와 사원을 한 꺼번에 가져오는 것!
  • 되도록 id는 개발자가 설정하는 것이 아니라 자동 생성(auto-increment)되도록 해주는 것이 성능 상 좋음

    • 수동으로 설정하면 id가 기존에 존재하는 값인지 검사하는 연산을 수행해야 하므로
  • category.getBoards() 와 같은 method를 사용하면 data가 수 십만 건이 될 수도 있음
    • 따라서 많이 사용되는 method는 아님
    • paging 처리해서 가져오는 것이 better!
      • 이 역시 repository를 통해 가져와야 함
    • 자료의 건수에 따라 전략을 잘 선택하자
  • FetchType.EAGER는 table 간 관계가 뗄 수 없는, 즉 반드시 함께 사용되야 할 때만 사용!
    • 기본은 LAZY로 쓰고 join하고 싶을 경우, repository에서 join 해서 사용
  • Repository에 Query method 생성 가능
    • method 이름을 보고 query 문이 자동으로 생성되게 됨
    • ex) BoardRepository에 findAllByName method 추가하면 이름과 관련한 SQL 검색 가능
  • JPA에 native query도 사용이 가능하나, 그럴 경우 해당 DBMS에 종속되는 것이기 때문에 추천하는 방법은 아님
    • JPA를 사용할 떄는 JPQL을 잘 다루는 것이 더 좋음
  • DBMS를 다루다 보면 Dynamic query도 존재할 수 있음
    • dynamic query: 조건에 따라 where절이 붙거나, 안 붙거나 하는 query
    • 이러한 Dynamic query는 Query method로 해결이 불가능
    • BoardRepositoryCustom과 같은 interface 생성해서 method 정의
    • BoardRepositoryImpl class를 생성해서 interface의 method 실 구현
      • proxy가 이 놈 사용(즉, BoardRepository와 BoardRepositoryImpl 사용)
    • 구현 방법
        1. EntityManager를 직접 주입하여, JPQL 조작 -> JPA 공부하면 상대적으로 수월
        • @Autowired
        • EntityManager
        • 문자열에서 오타 낼 가능성이 많음..
        1. JPQL을 다루기 위한 문법인 JPQL criteria 참조 -> 어려움
        1. QueryDSL 사용
        • Entity 정보들을 읽어들여 소스 코드 생성하는 라이브러리
        • ex) QBoard, QCategory...
        • 환경설정 어렵지만, 이후에 편리
          • maven 설정
    • 즉, BoardRepository는 JpaRepository와 BoardRepositoryCustom을 상속 받게 되는 것

etc

  • 커뮤니티가 많이 발달한 Java: 느리지만 지속적으로 발전하는 이유
    • 비슷한 기술이 등장하면 JSR에서 표준을 정해줌
    • 기술이 나오고 표준이 이후에 나오기 때문에, 동일한 기능을 하는 방식이 여럿 일 수 있음
  • 기술적 성숙도에 따라 아키텍쳐가 달라짐
    • JPA 같은 경우 난이도가 있기 때문에 주변에 같이 고민할 수 있는 사람이 있어야 함
    • 때문에 회사의 기술 stack을 보면 해당 회사의 기술적 수준을 알 수 있는 경우 많음


static 그리고 접근제한자

static

  • static 멤버는 class 안에 실제로 존재
    • 즉, object의 선언 없이 사용 가능한 멤버
public class Test{
    static int s = 0;
    int t = 0;

    public static void print1(){
        System.out.println("s = " + s);
//      System.out.println("t = " + t);
//      non-static member 접근 불가
    }

    public static void print2(){
        System.out.println("s = " + s);
        System.out.println("t = " + t);
    }
}
  • Java에서의 프로그램은 class들의 집합이기 때문에 main 함수 역시 class 내에 존재해야 함
    • 하지만 class의 객체 생성 없이 main method는 실행되어야 하기 때문에 항상 static으로 선언되어야 함
  • static 멤버 변수의 접근 방법?
    • 'object명'. 과 같이 해도 정상작동하는 경우 많으나, 엄격한 compile 규칙을 따른다면 틀린 것
    • 따라서 class에 속하는 멤버이므로, '클래스명'. 과 같이 접근하는 것이 정석
public class Test {
    static int s = 10;
    int t = 0;
    
    public static void print1() {
        System.out.println("s = " + s);
    }
    
    public void print2() {
        System.out.println("s = " + s);
        System.out.println("t = " + t);
    }
    
    public static void main(String[] args) {
        Test test1 = new Test();
        test1.t = 100;
        test1.print2

        // static 멤버 접근 방법
        Test.s = 100;
        Test.print1();
    }
}
  • static 멤버의 용도
    • main method
    • 상수 혹은 클래스 당 하나만 유지하고 있으면 되는 값
      • ex) Math.PI, Systemo.out
    • 순수하게 기능만으로 정의되는 method
      • ex) Math.abs(k), Math.sqrt(n), Math.min(a, b)



접근 제어: public, private, default, protected

  • public: 클래스 외부에서 접근이 가능
  • private: 클래스 내부에서만 접근 가능
  • default: 동일 패키지에 있는 다른 클래스에서 접근 가능
  • protected: 동일 패키지의 다른 클래스와 다른 패키지의 하위 클래스에서도 접근 가능

데이터 캡슐화

  • 모든 데이터 멤버를 private으로 만들고 필요한 경우 public한 get/set method를 제공
  • 객체가 제공해주는 method를 통하지 않고서는 객체 내부의 데이터에 접근할 수가 없음
    • 함부로 데이터를 접근하거나, 수정할 수 없음
  • 이것이 data encapsulation 혹은 information hiding


'Software Convergence > Java' 카테고리의 다른 글

Object 클래스와 Wrapper 클래스  (0) 2018.11.13
다형성: Polymorphism  (0) 2018.11.05
상속의 단점과 Strategy Pattern  (0) 2018.09.03
Generic 자료형 실습  (0) 2018.07.12
instanceof / encapsulation  (0) 2018.07.04

+ Recent posts