로봇청소기와 그리움
설에 가족 모두 울산에 같이 내려갔다가, 설 연휴 마지막 날에 아내와 첫째, 둘째를 놔두고 회사일로 나만 올라오게 되었다. 일주일 동안 회사일도 바쁘고, 감기 기운도 조금 있어서 일주일이 상당히 빠르게 흘렀다. 오늘 올라오고 있다는 전화 통화를 하고, 불현듯 일주일 동안 로봇청소기의 먼지통을 비우지 않았다는 생각이 들었다.
전기밥을 먹어야 하는 곳에서 열심히 충전하고 있는 로봇청소기의 뚜껑을 열고, 먼지통을 꺼내서, 다용도실로 걸어가면서 먼지통의 뚜껑을 열었는데, 먼지가 거의 모이지 않았다는 것을 알게 되었다. 비우려고 걸어가던 발걸음이 허탈하기도 했는데, 가장 먼저 든 생각은…
‘거봐, 우리 딸들이 그동안 먼지를 다 만드셨군!!’
조금 모인 먼지라도 쓰레기봉투에 버리고 청소솔로 내부를 깨끗하게 쓸어내면서, 다시 이런 생각이…
‘그래, 딸들이 독립을 하면, 로봇청소기 비울 일도 잘 없겠군!’
비운 먼지통을 들고 다시 로봇청소기에게 가서 먼지통을 넣으면서는 이런 생각이…
‘그런데, 딸들이 독립을 하고, 집에 아내와 둘 밖에 없으면, 먼지통의 먼지가 참 그립겠구나… ‘
Spring boot JPA
source code 는 아래 github 에서 받을 수 있습니다.
https://github.com/nanbean/springReact 의 spring-jpa branch해당 source code 받지 말고 아래대로 차근 차근 scratch 부터 만들어 보는 것을 추천합니다.
# 해당 source code 를 통해서 동작성을 빠르게 확인하려면,
$ git clone -b spring-jpa https://github.com/nanbean/springReact.git lcms
$ cd lcms
$ mvn spring-boot:run
이후 REST API 동작성 확인 으로 이동하시면 됩니다.
이전 spriing react 최신 기준으로 시작합니다.(Spring boot React)
spring-boot-devtools, h2, JPA, hsqldb, lombok 을 Dependency 에 추가
1. 이전 project 에서 pom.xml 의 dependency 에 spring-boot-devtools, h2, jpa, hsqldb, lombok 을 추가한다. dependencies 하위에 추가합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Content.java 추가
1. src/main/java/com/lge/lcms/content 디렉토리를 만들고 Content.java 파일을 하위에 생성합니다.
title 과 genre 만 가진 간단한 Entity 입니다.
package com.lge.lcms.content;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Column;
@NoArgsConstructor
@Table
@Getter
@Entity
public class Content {
@Id
@GeneratedValue
private Long id;
@Column
private String title;
@Column
private String genre;
@Builder
public Content(String title, String genre) {
this.title = title;
this.genre = genre;
}
}
ContentRepository.java 파일 추가
1. src/main/java/com/lge/lcms/content 하위에 ContentRepository.java 파일을 추가합니다.
package com.lge.lcms.content;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ContentRepository extends JpaRepository<Content, Long> {
}
ContentRepositoryTest.java 파일 추가
1. src/test/java/com/lge/lcms/content 하위에 ContentRepository.java 파일을 추가합니다.
package com.lge.lcms.content;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.junit.After;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ContentRepositoryTest {
@Autowired
ContentRepository contentRepository;
@After
public void cleanup() {
contentRepository.deleteAll();
}
@Test
public void getTest() {
contentRepository.save(Content.builder()
.title("Incredible")
.genre("Action")
.build());
List<Content> contentList = contentRepository.findAll();
Content content = contentList.get(0);
assertThat(content.getTitle(), is("Incredible"));
assertThat(content.getGenre(), is("Action"));
}
}
Test 실행
1. Test 를 통해서 Build 가 정상적으로 되는지 Test Result 가 정상인지 확인 합니다.
$ mvn test
...
...
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 29.654 s
[INFO] Finished at: 2019-01-30T13:36:29+09:00
[INFO] Final Memory: 42M/576M
[INFO] ------------------------------------------------------------------------
Rest API 추가
1. src/main/java/com/lge/lcms/web 디렉토리를 만들고 ContentRestController.java 파일을 하위에 생성합니다.
rest/content REST API 를 만듭니다.
package com.lge.lcms.web;
import com.lge.lcms.dto.ContentSaveRequestDto;
import com.lge.lcms.content.ContentRepository;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@AllArgsConstructor
@RequestMapping("/rest")
public class ContentRestController {
private ContentRepository contentRepository;
@PostMapping("/content")
public void savePosts(@RequestBody ContentSaveRequestDto dto){
contentRepository.save(dto.toEntity());
}
}
DTO 추가
1. src/main/java/com/lge/lcms/dto 디렉토리를 만들고 ContentSaveRequestDto.java 파일을 하위에 생성합니다.
View 를 생성합니다. View 와 Entity 는 구분하는 것이 좋습니다.
package com.lge.lcms.dto;
import com.lge.lcms.content.Content;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.Getter;
@Getter
@Setter
@NoArgsConstructor
public class ContentSaveRequestDto {
private String title;
private String genre;
public Content toEntity(){
return Content.builder()
.title(title)
.genre(genre)
.build();
}
}
REST API 동작성 확인
1. H2 활성화, src/main/resources/application.properties 에 아래 내용을 추가합니다.
H2 DB 를 웹브라우저를 통해서 접근하기 위해서입니다.
spring:
h2:
console:
enabled: true
2. spring boot 실행
$ mvn spring-boot:run
3. post 수행
$ curl --header "Content-Type: application/json" --request POST --data '{"title": "Incredible","genre": "Action"}' http://localhost:8080/rest/content
4. 입베디드 H2 consle 진입
브라우저 주소창에 http://localhost:8080/h2-console 을 입력합니다.
5. connect
JDBc URL 에 jdbc:h2:mem:testdb 을 입력하고 connect 를 눌러서 진입합니다.
7. query 를 통해서 POST API 동작 확인합니다.
아래 query 를 입력하고 Run 버튼을 누릅니다.
SELECT * FROM CONTENT;
Spring boot React
source code 는 아래 github 에서 받을 수 있습니다.
https://github.com/nanbean/springReact
해당 source code 받지 말고 아래대로 차근 차근 scratch 부터 만들어 보는 것을 추천합니다.
# 해당 source code 를 통해서 동작성을 빠르게 확인하려면,
$ git clone https://github.com/nanbean/springReact.git lcms
$ cd lcms
$ mvn clean install
$ java -jar target/lcms-0.0.1-SNAPSHOT.jar
후
browser 에서 http://localhost:8080/ 접속하면 됩니다.
Bootstrap 제작
spring 에서 제공하는 기본을 이용해서 skeleton code 를 만드는 과정입니다.
1. https://start.spring.io 에서 Gropu 에 com.lge, Artifact 에 lcms 입력, Web Dependencies 추가해서 Generate Project
2. 다운 받은 파일을 특정 폴더(spring-react)에 압축 풀기
tree 구조는 아래와 같습니다.
jeongsim.kim@ubuntu-n:~/work/lcms$ tree
.
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── lge
│ │ └── lcms
│ │ └── LcmsApplication.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── com
└── lge
└── lcms
└── LcmsApplicationTests.java
14 directories, 6 files
기본 Controller 추가
Rest API 를 추가하는 작업입니다. (/api/hello)
1. src/main/java/com/lge/lcms/HelloController.java 파일 생성
package com.lge.lcms;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
public class HelloController {
@GetMapping("/api/hello")
public String hello() {
return "Hello, the time at the server is now " + new Date() + "\n";
}
}
2. 실행
$ mvn spring-boot:run
3. 아래 curl 로 정상 동작 여부 확인 (다른 터미널에서)
$ curl http://localhost:8080/api/hello
Hello, the time at the server is now Tue Jan 29 14:41:10 KST 2019
React 추가
Front-end 로 React 를 사용하기 위해서 facebook 에서 만든 create-react-app 을 이용해서 React bootstrap 을 만드는 과정입니다.
1. 최상위 디렉토리에서 아래와 같이 frontend React Application 추가 (mvn spring-boot:run 종료하고)
$ create-react-app frontend
create-react-app 이 설치되어 있지 않다면?
$ npm install -g create-react-app
React 에 API 연결 추가
Front-end 인 React App 에서 spring 으로 Rest API 를 호출하여, 페이지에 그려주는 코드를 작성하는 것입니다.
1. frontend/src/App.js 파일을 아래와 같이 수정
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
state = {};
componentDidMount() {
setInterval(this.hello, 250);
}
hello = () => {
fetch('/api/hello')
.then(response => response.text())
.then(message => {
this.setState({message: message});
});
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">{this.state.message}</h1>
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
}
export default App;
Maven 으로 React Packaging 추가
maven 을 통해서 Front-end 인 React 를 build 하는 것입니다.
1. pom.xml 파일에 아래와 같이 plugin 추가(build/plugins 하위에 추가하면 됩니다.)
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.6</version>
<configuration>
<workingDirectory>frontend</workingDirectory>
<installDirectory>target</installDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v8.9.4</nodeVersion>
<npmVersion>5.6.0</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>npm run build</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
2. mvn clean install 이 정상적으로 되는지 확인 (Error 가 뜨는지 안 뜨는지?)
$ mvn clean install
3. tree 구조 확인
frontend 하위에 build 가 정상적으로 생성되는지?(maven 이 npm 을 호출해서 build 를 잘 했는지 확인)
$ tree frontend/build
frontend/build
├── asset-manifest.json
├── favicon.ico
├── index.html
├── manifest.json
├── precache-manifest.18b2b7a023755c5bb0bcd527b6c170b9.js
├── service-worker.js
└── static
├── css
│ ├── main.24e815be.chunk.css
│ └── main.24e815be.chunk.css.map
├── js
│ ├── 1.fa92c112.chunk.js
│ ├── 1.fa92c112.chunk.js.map
│ ├── main.21cc7467.chunk.js
│ ├── main.21cc7467.chunk.js.map
│ ├── runtime~main.229c360f.js
│ └── runtime~main.229c360f.js.map
└── media
└── logo.5d5d9eef.svg
4 directories, 15 files
Spring boot jar 에 React 추가
maven 을 통해서 만들어진 Front-end 리소스들을 jar 에 추가하는 작업입니다.
1. pom.xml 에 plugin 추가
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>generate-resources</phase>
<configuration>
<target>
<copy todir="${project.build.directory}/classes/public">
<fileset dir="${project.basedir}/frontend/build"/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
2. mvn clean install 이 정상적으로 되는지 확인 (Error 가 뜨는지 안 뜨는지?)
$ mvn clean install
3. jar 파일에 public resource 가 정상적으로 포함되어 있는지 확인
$ jar tvf target/lcms-0.0.1-SNAPSHOT.jar | grep public
0 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/
0 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/
0 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/media/
0 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/css/
0 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/js/
306 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/manifest.json
322072 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/js/1.fa92c112.chunk.js.map
1041 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/service-worker.js
7959 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/js/main.21cc7467.chunk.js.map
2828 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/css/main.24e815be.chunk.css.map
1502 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/js/runtime~main.229c360f.js
1706 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/js/main.21cc7467.chunk.js
606 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/precache-manifest.18b2b7a023755c5bb0bcd527b6c170b9.js
779 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/asset-manifest.json
2062 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/index.html
2671 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/media/logo.5d5d9eef.svg
3870 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/favicon.ico
984 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/css/main.24e815be.chunk.css
7996 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/js/runtime~main.229c360f.js.map
112436 Tue Jan 29 15:00:30 KST 2019 BOOT-INF/classes/public/static/js/1.fa92c112.chunk.js
실행 확인
Front-end 와 Back-end 가 정상적으로 동작하는지 확인하는 것입니다.
1. java 로 실행 확인
$ java -jar target/lcms-0.0.1-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.2.RELEASE)
...
2. browser 에서 정상 동작 확인
Zsh, Oh My ZSH 사용하기
zsh 설치
$ sudo apt install zsh
oh my zsh 설치
// curl 을 통해 설치
$ sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
// wget 을 통해 설치
$ sh -c "$(wget https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)"
// 둘 중 하나를 고르면 되고, 해당 내용은 https://ohmyz.sh/ 에 가이드가 있음
Oh My ZSH Theme 변경
$ vi ~/.zshrc
// ZSH_THEME 부분을 변경
ZSH_THEME="agnoster"