jetbrains - IntelliJ IDEA Ultimate 30% 할인된 금액으로 구매

Setup|2021. 8. 29. 00:24

jetbrains - IntelliJ IDEA Ultimate 30% 할인된 금액으로 구매

 

21년 8월30일까지 한국어언어팩 출시기념으로 30% 할인된 금액으로 판매하여 구매를 했습니다.

 

 

구매한 시점 버전으로 영구 사용할수 있는 라이선스 입니다

 

 

또한 IntelliJ IDEA Ultimate 에 vue.js 플러그인을 설치하면  vue.js 프로그램을 사용할수 있습니다. webstorm 구매할 필요없이

 

 IntelliJ IDEA Ultimate 하나로 java와 vue.js 모두 개발 가능합니다.

 

댓글()

HEXO 검색엔진최적화(Search Engine Optimization,SEO)

Setup/opensource|2021. 7. 28. 00:08

검색엔진 최적화(Search Engine Optimization,SEO)

내가 작성한 글이 검색엔진에게 잘 노출되기 위해서는 내 블로그가 검색엔진에 노출이 잘되도록 최적화 시켜야 됩니다. 가장 중요한건 블로그에 작성한 Post가 검색하는 사람들에게 가치를 줄수 있어야 합니다. 두번째는 구글,네이버,다음 검색엔진에게 내 블로그를 알려야 합니다. 내가 이렇게 정성스럽게 글을 잘 작성했으니 글을 읽어가서 사용자가 검색을 하면 내 블로그를 소개시켜 달라고 부탁하는 거죠. 내가 쓴글을 검색엔진이 읽어가기 쉽게 최적화하는걸 SEO라고 합니다. 오늘은 codingsarang 블로그를 최적화 시켜 보겠습니다.

 

hexo에서 제공해주는 플러그인 설치하기

hexo 에서는 SEO와 관련된 플러그인들이 다양하게 있습니다. https://hexo.io/plugins/ 접속하신후 seo라고 입력하고 검색을 수행하면 아래와 같은 다양한 plugin을 확인할수 있습니다. 그중 일반적으로 많이 사용하는 플러그인들을 설치해 보겠습니다.

  1. hexo-auto-canonical
  2. hexo-generator-robotstxt
  3. hexo-autonofollow
  4. hexo-generator-feed
  5. hexo-generator-seo-friendly-sitemap

hexo-auto-canonical 설치 및 적용

표준링크를 자동으로 생성해주는 플러그인입니다. 아래 명령어를 입력하여 설치를 진행합니다.

npm install --save hexo-auto-canonical

설치가 완료되면 themes > hueman > layout > common > head.ejs 에 아래에 코드를 입력합니다.

... <%- meta(page) %> <%- autoCanonical(config, page) %> ...

블로그에서 소스보기를 하고 아래와 같은 내용을 확인할수 있다면 이제부터 검색엔진은 이글의 표준링크에 대해서 인식을 하게 됩니다.

<link rel="canonical" href="https://open.github.io/2020/07/06/hello-world/">

hexo-generator-robotstxt 설치 및 적용

robots.txt 의 상세 내용은 https://support.google.com/webmasters/answer/6062608?hl=ko&ref_topic=6061961 여기에서 확인 가능합니다.

robots.txt 파일이란 무엇인가요? robots.txt 파일은 검색엔진 크롤러에서 사이트에 요청할 수 있거나 요청할 수 없는 페이지 또는 파일을 크롤러에 지시하는 파일입니다. 이 파일은 요청으로 인해 사이트가 오버로드되지 않도록 하는 데 주로 사용되며, Google로부터 웹페이지를 숨기는 데 사용하는 메커니즘이 아닙니다. 웹페이지가 Google에 표시되지 않도록 하려면 noindex 명령어를 사용하거나 페이지를 비밀번호로 보호해야 합니다.

robots.txt의 용도는?
robots.txt는 주로 사이트의 크롤러 트래픽을 관리하는 데 사용되며 보통 다음과 같이 파일 형식에 따라 Google에서 페이지를 숨기는 데 사용됩니다.

blog 홈디렉토리에서 아래 명령어를 입력합니다.

 

npm install hexo-generator-robotstxt --save

blog 홈디렉토리에 있는 _config.yml에 아래와 같이 입력합니다. 여기에서 *가 의미하는 내용은 모든 사용자 에이전트(robot)가 전체 사이트에 액세스할 수 있습니다.

robotstxt: ​​useragent: "*" ​​allow: ​​​​- / ​​sitemap: https://yourblogsite.github.io/sitemap.xml

hexo-autonofollow 설치및 적용

외부 링크에 아래 처럼 nofollow 속성을 추가하는 플러그인 입니다.

<meta name="robots" content="nofollow" />

특정 페이지의 링크를 추적하지 않도록 하거나 특정 링크의 추적을 막을때 nofollow 속성값을 사용 할 수 있습니다. 검색엔진마다 nofollow 속성을 처리하는 방식이 다를 수 있습니다.

이 페이지의 링크를 추적하지 않도록 설정할때 :

<meta name="robots" content="nofollow" />

특정 링크의 추적을 하지 않도록 설정할때 :

<a href="http://www.test.com/" rel="nofollow">링크</a>

보통 신뢰할 수 없는 콘텐츠나 유료 링크의 경우 다른 사용자의 검색에 노출되지 않아야 하므로 nofollow 속성을 사용하는 것이 좋습니다. 해당 플러그인을 enable 해놓으면 모든 외부 링크에 rel=”external nofollow” 속성을 자동으로 추가합니다. 외부링크에만 동작하기 때문에 본인 사이트의 도메인 링크는 제외됩니다. 외부 링크에 target=”_blank” 속성을 넣어서 클릭할 경우 새로운 탭 또는 윈도우에서 열리게 합니다.

아래 명령어를 입력하여 설치 할수 있습니다.

 

npm install hexo-autonofollow --save

_config.yml 에 아래와 같이 입력합니다.

nofollow: ​​​​enable: true ​​​​exclude: ​​​​- exclude1.com ​​​​- exclude2.com

hexo-generator-feed

자동으로 RSS feed를 생성해주는 플러그인 입니다.

어떤 사이트가 있을 때, 그 사이트를 매일 방문해서 재미있는 새로운 기사가 있는지 확인하는 것은 번거롭습니다. 특히 새 기사가 매일 또는 정기적으로 올라오는 것이 아니라 불규칙할 때는 더욱 그렇습니다. 그 사이트를 직접 방문하지 않고, 새 기사들만 자신의 컴퓨터로 “배달”이 된다면 편리할 것입니다. RSS(Really Simple Syndication 의 약자) 같은 “사이트 피드”란, 새 기사들의 제목만, 또는 새 기사들 전체를 뽑아서 하나의 파일로 만들어 놓은 것입니다. 이제 각 사이트들에서 제공하는 RSS파일 주소만 수집하여 확인하면, 자신의 취향에 맞는 새로운 읽을거리를 쉽게 찾아서 읽을 수 있습니다. 그러나 모든 사이트에서 RSS피드를 제공하는 것은 아닙니다. 1년 내내 새로운 내용이 없는 정적인 사이트에서는 제공하지 않는 것이 보통입니다. 새로운 읽을거리가 자주 올라오는 “뉴스형”, “블로그형” 사이트에서 주로 제공됩니다.
RSS Feed

아래 명령어를 입력하여 설치 할수 있습니다.

npm install hexo-generator-feed --save

_config.yml 에 아래와 같이 입력합니다.

feed: ​​type: rss2 ​​path: rss2.xml ​​limit: 20

type은 feed의 종류입니다. (atom/rss2) - * 네이버는 atom을 지원하지 않으므로 rss2로 지정합니다.
path는 feed가 생성될 경로 입니다.(default : atom.xml, rss2.xml)
limit는 최신 포스트 수 설정 (0 또는 false - 전체 포스트)

hexo-generator-seo-friendly-sitemap

크롤러가 블로그를 더욱 효율적으로 클롤링 할 수 있도록 사이트맵 xml 파일을 자동으로 생성해 줍니다. 이렇게 생성된 사이트맵을 robot이 가져가서 색인을 진행합니다.

아래 명령어를 입력하여 설치 할수 있습니다.

npm install hexo-generator-seo-friendly-sitemap --save

_config.yml 에 아래와 같이 입력합니다.

sitemap: ​​path: sitemap.xml ​​tag: false ​​category: false

path 는 sitemap이 생성될 경로를 말하며 tag 및 category 포함 여부를 지정할수 있습니다. 전 제외 시켰습니다.

검색엔진 등록하기

아래 url을 클릭하여 구글 애널리틱스에 회의 가입후 추적 아이디를 발급받습니다.
구글 애널리틱스

발급받은 아이디는 themes의 _config.yml 내 google_analytics에 넣어 줍니다.

_config.yml 파일을 아래와 같이 수정하세요.

plugins: ​​​​google-analytics: ​​​​​​​​# Google Analytics tracking id ​​​​​​​​tracking_id: UA-*********-1

아래 url에 접속하여 사이트 맵을 제출합니다.
google search console

가입 후 사이트를 추가 합니다. 생성된 html파일을 루트에 올린 후 확인 하는 방법과 애널리스틱 가입을 확인하는 방법으로 인증을 진행할수 있습니다.
Sitemaps 메뉴에 위에서 생성한 sitemap.xml과 rss2.xml을 추가해 주세요. sitemap을 등록하고 일정시간 지나면 내 사이트에 등록된 글을 구글
검색을 통해 확인할수 있습니다.

아래 사이트를 클릭하여 네이버에 등록을 진행하겠습니다.

NAVER 웹마스터도구

먼저 사이트 소유확인 부터 진행해 주세요 저는 meta태그정보를 블로그에 head.ejs에 추가하였습니다.

<meta charset="utf-8" /> <meta name="naver-site-verification" content="d81165627f54ef03c0e3669744894f14e24d39ad" />

네이버 웹마스터 도구 페이지에서 사이트 간단 체크하기 메뉴를 통해 현재 블로그의 최적화 상태를 알아볼 수 있습니다.

 

연동 사이트 목록 페이지 내 사이트 추가에 블로그 주소를 추가해 줍니다.

 

다음 검색 등록에서 신규등록을 진행합니다.등록한 이메일 접수완료 메일을 받을 수 있습니다.
[다음 검색 등록](https://register.search.daum.net/index.daum,"다음 검색 등록”)

 

 

댓글()

HEXO와 Github page를 이용한 무료 정적 웹사이트(Blog) 만들기

Setup/opensource|2021. 7. 27. 23:53

개요

정적 웹사이트 생성기 hexo를 이용해서 블로그를 만든후 github page까지 반영하는 방법에 대해서 알아보겠습니다. 코딩사랑 블로그를 만들면서 아래 내용되로 설치를 했구요 차근차근 따라오시다 보면 github.io도메인을 가지는 블로그를 만들수 있습니다.

정적 웹사이트 ?

정적 웹사이트는 컨텐츠 내용이 변하지 않는 정적인 컨텐츠를 사용하는 웹사이트 입니다. 정적인 컨텐츠는 html,css,javascript,image 와 같은 컨텐츠로써 클라이언트 웹브라우저가 서버에 내용을 요청하면 서버에 등록되어 있는 파일 그대로를 서비스 하기에 모든 클라이언트에게 동일한 결과를 리턴합니다. 동적인 컨텐츠는 jsp,servelt,php,nodejs 같은 컨텐츠로써 요청에 따라 다른 결과 내용을 보여줍니다. 예를 들어 로그인 하기전에는 내용이 보이지 않았다가 로그인하면 내용을 보여주고 사용자에 따라 다른 내용을 보여주는 것들도 모두 동적인 컨텐츠입니다.

정적인 웹사이트는 동적인 웹사이트보다 속도가 빠릅니다. 일반적인 블로그를 한번 살펴볼까요? 블로그에서 글을 쓰면 내가쓴글은 db에 저장이 됩니다. 글 읽기에 관련 된 사용자 요청이 오면 db에서 데이터를 읽어서 html로 변환해서 사용자의 웹브라우저에 보여주는 구조 입니다. 하지만 정적인 웹사이트는 이미 html이 다 만들어져 있기에 페이지를 만들기 위해 이루어 지는 일련의 과정이 없어서 속도가 빠릅니다.

하지만 단점도 있습니다. 반복되는 부분의 코드를 작성해야 된다는 거죠. 보통의 웹사이트나 블로그를 보면 상단부분의 메뉴와 하단 부분은 변하지 않습니다. 그래서 php에서는 header.php footer.php 이런 공통 파일들을 만들어서 include해서 사용 하죠 그리고 메뉴 부분은 상단 부분을 클릭할때 마다 다르기에 db에서 내용을 가져와서 만들거나 코드를 작성해서 메뉴를 만듭니다. 그런 부분을 정적웹사이트에서 구현할려면 한페이지마다 상단,좌측,하단 내용을 모두 중복해서 코딩해야됩니다. 생산성도 좋지않고 유지보수도 하기 힘듭니다. 속도는 빠르게 하고 중복되는 코드들은 줄이고 유지보수를 편하게 하기 위해서는 어떻게 해야 되나 사람들은 고민하게되고 이를 해결하기 위해 hexo 나 jekyll 같은 정적 웹사이트 생성기를 사용합니다.

아래 코드는 지금 보시고 있는 blog에서 사용하는 head.ejs 입니다. 가장 상단에 공통으로 사용하는 css나 자바스크립트 같은걸 포함하고 post마다 글을 바꿀수 있게 코딩이 되어 있습니다. asp나 php같은 스크립트 언어와 비슷하죠? 내 pc에서 글을쓸때는 동적으로 컨텐츠들이 생성됩니다. 생성된 컨텐츠를 build라는 과정을 거쳐서 정적인 컨텐츠로 변환하고 변환된 컨텐츠를 서버에 올리면 정적인 웹사이트가 됩니다.

 

<!DOCTYPE html> <html> <head> ​​​​<meta charset="utf-8" /> ​​​​<%- partial('pwa/index') %> ​​​​<% ​​​​​​​​var title = page.title; ​​​​​​​​if (is_archive()) { ​​​​​​​​​​​​title = __('index.archive'); ​​​​​​​​​​​​if (is_month()) { ​​​​​​​​​​​​​​​​title += ': ' + page.year + '/' + page.month; ​​​​​​​​​​​​} else if (is_year()) { ​​​​​​​​​​​​​​​​title += ': ' + page.year; ​​​​​​​​​​​​} ​​​​​​​​} else if (is_category()) { ​​​​​​​​​​​​title = __('index.category') + ': ' + page.category; ​​​​​​​​} else if (is_tag()) { ​​​​​​​​​​​​title = __('index.tag') + ': ' + page.tag; ​​​​​​​​} ​​​​%> ​​​​<title><% if (title) { %><%= title %> | <% } %><%= config.title %></title> ​​​​<% if (theme.plugins.bing_site_verification) { %> ​​​​​​<meta name="msvalidate.01" content="<%- theme.plugins.bing_site_verification %>" /> ​​​​<% } %> ​​​​<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> ​​​​<% if (page.tags) { ​​​​​​​​var keywords = []; ​​​​​​​​for (var i in page.tags.data) { ​​​​​​​​​​​​keywords.push(page.tags.data[i].name); ​​​​​​​​} ​​​​%> ​​​​<meta name="keywords" content="<%= keywords.join(',') %>" /> ​​​​<% } %> ​​​​<%- open_graph({ ​​​​​​​​image: thumbnail(page), ​​​​​​​​fb_app_id: theme.miscellaneous.open_graph.fb_app_id, ​​​​​​​​fb_admins: theme.miscellaneous.open_graph.fb_admins, ​​​​​​​​twitter_id: theme.miscellaneous.open_graph.twitter_id, ​​​​}) %> ​​​​<%- meta(page) %> ​​​​<% if (theme.customize && theme.customize.social_links && theme.customize.social_links.rss) { %> ​​​​​​​​<link rel="alternate" href="<%- theme.customize.social_links.rss %>" title="<%= config.title %>" type="application/atom+xml" /> ​​​​<% } %> ​​​​<% if (theme.customize.favicon) { %> ​​​​​​​​<link rel="icon" href="<%- url_for(theme.customize.favicon) %>" /> ​​​​<% } %> ​​​​<%- css('libs/font-awesome/css/font-awesome.min') %> ​​​​<%- css('libs/titillium-web/styles') %> ​​​​<%- css('libs/source-code-pro/styles') %> ​​​​<%- css('css/style') %> ​​​​<%- js('libs/jquery/3.5.0/jquery.min') %> ​​​​<%- partial('plugin/scripts', { isHead: true }) %> ​​​​<%- css('https://cdn.rawgit.com/innks/NanumSquareRound/master/nanumsquareround.css') %> </head>

hexo + github 를 사용하면 어떤 이점이 있나요?

블로그 툴들은 많고(naver,tistory,wordpress..) 왜 어렵게 이런툴을 사용해야 되나요? 글쓰는건 네이버,티스토리,wordpress로 쓰는게 편합니다. 저는 프로그래머 입니다. 제가 만들 프로그램들 주로 FrontEnd쪽 application입니다. 위에 블로그들에서는 이를 보여주는게 한계가 있습니다. 해당 블로그들이 제공해주는 범위 내에서 사용할수 밗에없는데 hexo 같은 정적 웹사이트 생성기는 컨텐츠를 생성하는 사람들이 생각하는되로 자유롭게 내용을 꾸밀수 있습니다. 그리고 내가 만든 웹 어플리케이션을 올릴수도 있습니다. 이런 부분때문에 정적 웹사이트 생성기를 선택하게 되었습니다.

jekyll + github도 조합으로 정적인 웹사이트를 만들수 있습니다. 저도 이전 블로그에서 jekyll + github조합으로 사용했습니다. hexo + github 조합이 더 편리하다는 의견들이 많아서 이번에 저도 도전해 보았습니다. 저의 글을 끝까지 잘 읽으시면 30분만에 hexo + github 조합으로 Blog를 만들수 있습니다. 끝까지 읽어 주실거죠 미리 감사 인사 드립니다.

설치방법

새로운 언어를 배우거나 툴 사용법을 배울때 가장 좋은 방법은 무작정 설치를 해보고 이것저것 만들어 보는 방법이 가장 좋습니다. 어느정도 이론이나 아키텍처를 알고 시작하면 더 좋겠지만, 저같은 경우에는 우선 써보고 나서 구조는 뒤에 차근차근 알아가는게 좋았습니다. 제가 이걸 계속 사용할지도 의문이고, 어렵다고 생각이 들면 사용하지 않게 됩니다. 무모하게 시작하는것도 새로운 기술을 배울때는 좋은 접근법이라고 생각합니다.

설치순서

  • github 계정만들기
  • node js 설치
  • git-scm 설치
  • hexo 설치 및 구동
  • hexo 테마적용하기
  • 새글 써보기
  • github repository 생성
  • hexo 환경설정
  • hexo 배포하기(deploy)

github 계정만들기

http://www.github.com 접속하세요 그리고 sing up for github버튼을 클릭하여 계정을 생성해 주세요.

아래와 같이 항목을 선택하고 complete버튼을 클릭하면 등록하신 이메일로 인증 메일이 발송됩니다.

받은 메일함에서 Verify email address 버튼을 클릭하여 이메일 인증을 완료합니다.

인증이 완료되면 아래와 같은 메시지를 볼수 있습니다.

여기까지 진행하면 github 계정만들기가 완료 됩니다.

 

nodejs 설치하기

아래 사이트에서 nodejs 다운로드 받습니다. 글을 쓰고있는 시점에서 안정적,신뢰도가 높은 LTS버전을 다운로드 받습니다.

 

next 버튼을 클릭해서 설치를 진행합니다.

설치가 완료되면 finish 버튼을 클릭하여 설치를 완료합니다.

윈도우 버튼을 클릭하여 Node.js 가 설치된걸 확인할수 있습니다.

git-scm 설치하기

github를 사용하기 위해서는 git-scm을 설치해야 됩니다. https://git-scm.com/ 접속하시고 64-bit git for windows setup 링크를 클릭 하여 설치 파일을 다운로드 받습니다.

다운로드한 파일을 클릭하여 설치 를 진행합니다. next 버튼을 클릭합니다.

Next를 눌러서 진행합니다. 설치가 완료되었습니다.

hexo 설치 및 서버 구동

  1. command창을 하나 뛰운다음 c:\로 이동합니다.
  2. npm (node package manager)로 hexo-cli를 -g 전역으로 설치합니다.
  3. blog를 초기화 합니다. 이름은 blog
  4. blog 폴더로 이동한후 npm install 명령을 실행합니다.
  5. 설치가 완료되면 hexo 서버를 뛰웁니다. (hexo server)
npm install hexo-cli -g hexo init blog cd blog npm install hexo server

 

hexo 테마적용하기

https://hexo.io/themes/ 에서 hexo 다양한 테마를 다운로드 받아 적용할수 있습니다. 코딩사랑에서 사용하는 theme은 https://github.com/ppoffice/hexo-theme-hueman 이곳의 theme을 이용했습니다. theme을 적용하는 방법은 해당 theme사이트로 가면 설치방법을 알수 있습니다. 전 이문서를 https://github.com/ppoffice/hexo-theme-hueman/wiki/Installation 이용해서 설치를 진행했습니다.

아래 명령어로 theme을 복제합니다.
git clone https://github.com/ppoffice/hexo-theme-hueman.git themes/hueman

 

C:\blog>cd blog C:\blog>#git clone https://github.com/ppoffice/hexo-theme-hueman.git themes/hueman Cloning into 'themes/hueman'... remote: Enumerating objects: 40, done. remote: Counting objects: 100% (40/40), done. remote: Compressing objects: 100% (30/30), done. remote: Total 1937 (delta 0), reused 37 (delta 0), pack-reused 1897 Receiving objects: 100% (1937/1937), 5.66 MiB | 4.10 MiB/s, done. Resolving deltas: 100% (1015/1015), done.

 

c:\blog 폴더의 _confi.yml파일에서 theme을 파일을 열어서 아래와 같이 설치한 theme명으로 수정한다.

... # Extensions ## Plugins: https://hexo.io/plugins/ ## Themes: https://hexo.io/themes/ theme: hueman ...

 

 

themes\hueman 디렉토리로 이동한후 _config.yml.example 파일일 _config.yml로 수정한다. 수정 완료후 dir 명령어를 입력하여 정상적으로 파일명이 변경되었는지 확인한다.

 

C:\blog>cd themes\hueman C:\blog\themes\hueman>rename _config.yml.example _config.yml C:\blog\themes\hueman>dir ​C 드라이브의 볼륨에는 이름이 없습니다. ​볼륨 일련 번호: 6E7E-7D01 ​C:\blog\themes\hueman 디렉터리 2020-07-05 오후 10:05 <DIR> . 2020-07-05 오후 10:05 <DIR> .. 2020-07-05 오후 08:44 <DIR> .github 2020-07-05 오후 08:44 11 .gitignore 2020-07-05 오후 08:44 <DIR> languages 2020-07-05 오후 08:44 <DIR> layout 2020-07-05 오후 08:44 18,401 LICENSE 2020-07-05 오후 08:44 173 package.json 2020-07-05 오후 08:44 2,946 README.md 2020-07-05 오후 08:44 <DIR> scripts 2020-07-05 오후 08:44 <DIR> source 2020-07-05 오후 08:44 5,551 _config.yml ​​​​​​​​​​​​​​​5개 파일 27,082 바이트 ​​​​​​​​​​​​​​​7개 디렉터리 218,937,688,064 바이트 남음 C:\blog\themes\hueman>

 

c:\blog 디렉토리로 이동한후 hexo server하여 블로그를 구동시켜서 아래와 같은 화면이 나오면 설치가 완료되었습니다.

C:\blog\themes\hueman>cd .. C:\blog\themes>cd .. C:\blog>hexo server INFO Start processing INFO Hexo is running at http://localhost:4000 . Press Ctrl+C to stop.

blog가 제되로 나오는지 확인해 보겠습니다. http://localhost:4000 을 클릭하여 아래와 같은 그림이 나온다면 성공입니다.

 

 

새글 써보기

새로운 cmd 창을 하나더 열어서 blog폴더로 이동한후 hexo new “test1” 라고 입력합니다.

C:\blog>hexo new "test1" INFO Created: C:\blog\source\_posts\test1.md

크롬에서 새로고침을 하면 아래와 같이 test1이라는 새글이 등록됨을 알수 있습니다.

editor로 c:\blog\source\test1.md파일을 열어서 아래와 같이 입력후 내용을 저장하세요

아래내용을 입력합니다.

# 글을 쓰는 방법 입니다. 글은 markdown이라는 문법을 알아야 됩니다. [![Vue](https://kr.vuejs.org/images/logo.png)](https://kr.vuejs.org/)

test1 글 상세보기를 하면 아래와 같은 내용을 확인할수 있습니다.

github repository 생성

https://github.com/ 에 접속하여 우측 상단의 + 버튼을 클릭하고 New repository 메뉴를 선택합니다.

Repository Name을 “계정명.github.io”라고 입력하고 Initialize this repository with a README 부분에 체크하고 Create repository 버튼을 클릭합니다. 이렇게 만들면 http://codingsarnag.github.io 로 접속하면 웹사이트에 접근이 가능합니다.

hexo 환경설정

블로그 환경설정을 해보겠습니다. c:\blog\_config.xml파일을 수정합니다. 제일 아래쪽에 deploy 정보는 본인 github repository 명을 입력하셔야 됩니다. 이 정보 기준으로 github page에 hexo를 deploy 합니다.

# Site title: 코딩사랑 subtitle: '문제 해결능력을 향상을 위한 방법과 프로그래밍 언어에 대해서 다룹니다.' description: '문제 해결능력을 향상을 위한 방법과 프로그래밍 언어에 대해서 다룹니다.' keywords: coding,programming,frontend,backend,opensource author: codingsarang language: ko timezone: 'asia/seoul' ... 중략 ... # URL ## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/' url: https://codingsarang.github.io/ ... 중략 ... deploy: ​​type: git ​​repo: https://github.com/codingsarang/codingsarang.github.io ​​branch: master

themes/hueman/_config.xml 파일내용도 본인 환경에 맞게 수정해주세요

 

.hexo 배포하기(deploy)

 

이제 정말 마지막 입니다. 내가 작성한 글들을 정적인 contents로 generate한후 github page에 배포를 진행하면 내가 쓴글을 온라인 에서 확인 가능합니다.

먼저 hexo generate 명령어를 실행하면 아래와 같은 결과를 확인할수 있습니다.

 

hexo generate INFO Start processing INFO Files loaded in 226 ms INFO Generated: content.json INFO Generated: categories/OPENSOURCE/index.html INFO Generated: categories/ETC/index.html INFO Generated: 2020/07/02/hexo-github/index.html INFO Generated: index.html INFO Generated: 2020/07/01/hello-world/index.html INFO Generated: archives/2020/07/index.html INFO Generated: archives/index.html INFO Generated: archives/2020/index.html INFO Generated: images/repo1.png INFO Generated: images/repo2.png INFO Generated: images/repo3.png INFO 12 files generated in 60 ms

 

이상태에서 hexo deploy를 실행합니다. 이렇게 실행하면 최초 git에 로그인을 하는데 본인계정정보와 패스워드를 입력하여 인증을 하고 나면 그다음부터 deploy할때는 정보를 물어보지 않습니다. deploy (commit)가 성공하면 아래와 같은 결과를 확인할수 있습니다.

 

hexo deploy INFO Deploying: git INFO Clearing .deploy_git folder... INFO Copying files from public folder... INFO Copying files from extend dirs... [master 0626b12] Site updated: 2020-07-06 00:16:13 ​12 files changed, 38 insertions(+), 28 deletions(-) ​create mode 100644 images/repo1.png ​create mode 100644 images/repo2.png ​create mode 100644 images/repo3.png git: 'credential-manager' is not a git command. See 'git --help'. Enumerating objects: 50, done. Counting objects: 100% (50/50), done. Delta compression using up to 6 threads Compressing objects: 100% (19/19), done. Writing objects: 100% (27/27), 540.77 KiB | 28.46 MiB/s, done. Total 27 (delta 10), reused 0 (delta 0) remote: Resolving deltas: 100% (10/10), completed with 8 local objects. To https://github.com/codingsarang/codingsarang.github.io ​​​c3f2489..0626b12 HEAD -> master Branch 'master' set up to track remote branch 'master' from 'https://github.com/codingsarang/codingsarang.github.io'. INFO Deploy done: git

사이트에 접속하시면 아래와 같이 작성한 글을 확인할수 있습니다.

댓글()

정적 웹사이트 생성시 Jekyll 설치 및 간단 사용법

Setup/opensource|2021. 7. 27. 23:32

정적 웹사이트 생성시 Jekyll 설치 및 간단 사용법

개요

Jekyll은 정적(static) 웹사이트 생성기 입니다. Markdown이란 문법으로 문서를 작성하고 이를 build를 시켜주면 정적 웹페이지(html) 를 생성해 줍니다. 그리고 php 같은 언어를 사용할때 header.php footer.php 같은걸 만들어 새로운 페이지를 만들때 위에 두파일을 include하는데 지킬또한 이런걸 지원해 줍니다. tistory가 많이 좋아져서 귀찮게 저런걸 써야 하는 생각도 있었지만 javascript 예제코드를 작성하고 실행할때 즉 웹어플리케이션 데모를 보여줄때는 필요할것 같아서 Jekyll 설치 방법에 대해서 정리하고 실제로 정적 웹사이트를 만들어 볼려고 합니다. 그리고 티스토리 같은경우에는 일일 발행할수 있는 글수가 15개로 제한되는데 지킬로 문서를 작성하고 github에 publish를 수행하면 이런 제한도 받지 않습니다. 사실 하루에 글 하나 적는것도 힘들긴 하지만 … 만약 글쓰고 싶은날 제한에 걸리면 한번쯤은 사용할수 있을것 같네요.

여담이지만 본 블로그를 jekyll로 구성할려고 자료를 정리했다가 hexo로 변경하여 설치하면서 정리했던 내용을 기록으로 남깁니다.

설치

jekyll을 사용하기 위해서는 루비를 설치해야 됩니다. 각각의 OS 별 루비 설치는 아래 링크를 확인하세요 전 mac을 사용 하므로 mac설치법을 보고 그대로 진행 하겠습니다. https://jekyllrb-ko.github.io/docs/installation/macos/
https://jekyllrb-ko.github.io/docs/installation/

 

맥OS 카탈리나 버전에서는 루비가 기본 탑재되어 있다고 하네요… 그래서 바로 지킬 설치를 진행하겠습니다.

$ gem install bundler jekyll

설치가 완료되고 나면 아래와 같은 결과를 확인할수 있습니다.

Successfully installed bundler-2.1.4 Parsing documentation for bundler-2.1.4 Done installing documentation for bundler after 2 seconds Successfully installed jekyll-4.1.0 Parsing documentation for jekyll-4.1.0 Done installing documentation for jekyll after 0 seconds 2 gems installed

 

우선 설치까지만 하고 github 사이트에 내계정명과 동일한 repository를 만듭니다.
Github 계정생성하는 부분은 https://codingsarang.github.io/2020/07/08/opensource/githubCreate/ 링크를 클릭하여 확인 하세요

우측 상단의 + 버튼을 클릭하고 New repository 메뉴를 선택합니다.

Repository Name을 “계정명.github.io”라고 입력하고 Initialize this repository with a README 부분에 체크하고 Create repository 버튼을 클릭 합니다.

Create new file 버튼을 클릭합니다.

New file name 에는 index.html을 입력하고 edit new file에 아래와 같은 html을 작성한후 commit 로그를 작성하고 commit new file버튼을 클릭하여 index.html 을 생성합니다.

html 내용

<html lang="en"> <head> ​​​​<meta charset="UTF-8"> ​​​​<meta name="viewport" content="width=device-width, initial-scale=1.0"> ​​​​<title>Document</title> ​​​​<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> ​​​​<div id="root"> ​​​​​​​​<div v-if="liked">좋아요 눌렀음</div> ​​​​​​​​<button v-else v-on:click="onClickButton">like</button> ​​​​</div> </body> <script> ​​​​const app = new Vue({ ​​​​​​​​el: '#root', ​​​​​​​​data: { ​​​​​​​​​​​​liked : false, ​​​​​​​​}, ​​​​​​​​methods: { ​​​​​​​​​​​​onClickButton() { ​​​​​​​​​​​​​​​​this.liked = true; ​​​​​​​​​​​​} ​​​​​​​​}, ​​​​}); </script> </html>

아래처럼 https://계정명.github.io/index.html을 실행하면 아래와 같은 화면이 출력됩니다.(참고로 위 index.html vue.js 를 이용하여 만들었음)

 

처음부터 끝까지 블로그를 만드는건 비효율적이라 생각하여 나와 맞는 테마를 찾아서 github clone을 통하여 초기 셋업을 진행합니다.
아래 사이트를 그래로 복사해서 진행하겠습니다.

https://github.com/passbolt/passbolt_help

※ 해당 템플릿은 Passbolt help code is distributed under the Affero General Public License v3 Passbolt help text is distributed under Creative Common BY-SA-4.0 이런 라이선스 정책을 가지고 있어 사용해도 문제가 되지 않습니다.

Creative Common BY-SA-4.0 라이선스 정책

아래 명령어를 입력하여 복제를 진행 합니다.

git clone https://github.com/passbolt/passbolt_help.git

위에 명령어를 실행하면 아래와 같이 정상 복제 되었음을 확인할수 있습니다.

Cloning into 'passbolt_help'... remote: Enumerating objects: 1015, done. remote: Counting objects: 100% (1015/1015), done. remote: Compressing objects: 100% (546/546), done. remote: Total 10735 (delta 741), reused 665 (delta 443), pack-reused 9720 Receiving objects: 100% (10735/10735), 37.08 MiB | 9.08 MiB/s, done. Resolving deltas: 100% (8301/8301), done.

복제된 디렉토리로 이동후 jekyll build 수행합니다.

cd passbolt_help bundle exec jekyll build

실행결과

Configuration file: /Users/opensourcereporter/passbolt_help/_config.yml ​​​​​​​​​​​​Source: /Users/opensourcereporter/passbolt_help ​​​​​​​Destination: docs ​Incremental build: disabled. Enable with --incremental ​​​​​​Generating... ​​​​​​​Jekyll Feed: Generating feed for posts ​​​​​​​​​​​​​​​​​​​​done in 3.849 seconds. ​Auto-regeneration: disabled. Use --watch to enable.

서버실행

bundle exec jekyll serve

실행결과

​​Server running... press ctrl-c to stop. [2020-06-17 00:30:47] ERROR Errno::ECONNRESET: Connection reset by peer @ io_fillbuf - fd:19 ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/httpserver.rb:82:in `eof?' ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/httpserver.rb:82:in `run' ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/server.rb:307:in `block in start_thread' [2020-06-17 00:30:47] ERROR Errno::ECONNRESET: Connection reset by peer @ io_fillbuf - fd:20 ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/httpserver.rb:82:in `eof?' ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/httpserver.rb:82:in `run' ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/server.rb:307:in `block in start_thread' [2020-06-17 00:30:47] ERROR Errno::ECONNRESET: Connection reset by peer @ io_fillbuf - fd:18 ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/httpserver.rb:82:in `eof?' ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/httpserver.rb:82:in `run' ‌/Users/comganet/.rvm/rubies/ruby-2.5.8/lib/ruby/2.5.0/webrick/server.rb:307:in `block in start_thread'

 

웹 브라우저에서 확인하면 아래와 같은 화면을 확인할수 있습니다.
http://127.0.0.1:4000/

 

이제 복제된 파일들을 내 github 계정에 올립니다.

Command 스페이스바를 눌러서 github desktop을 실행 합니다.

github desktop설치는 아래 post를 참고하세요

https://codingsarang.github.io/2020/07/10/opensource/githubDesktop/

아래 그림과 같이 추가된 파일들을 publish하기위해 아래 그림처럼 적당하게 commnet를 입력하고 commit to master 버튼을 클릭 합니다.

 

그리고 나서 우측에 Pull Origin버튼을 클릭 합니다.

Pull origin

아래 사이트 들어가면 로컬에서 생성된것과 동일한 github 페이지를 확인할수 있습니다.
https://계정명.github.io/index.html

댓글()

Github Desktop 설치

Setup/opensource|2021. 7. 27. 23:20

Github Desktop 설치

Github Desktop을 설치해서 사용하시면 콘솔창을 통해서 git 명령어를 입력하는것 보다 훨씬 편리한 방법으로 github에 내용을 올릴수 있습니다. Github desktop 실행시켜 변경된 사항이 있으면 한눈에 확인하고 간단하게 마우스 클릭만으로 소스를 올릴수 있는 Github Desktop 설치법과 간단 사용법에 대해서 알아 보겠습니다.

 

설치방법

https://desktop.github.com/ url에 접속하여 파일을 다운로드 받습니다.

 

다운로드 받은 파일을 실행하고 잠시 기다리면 설치가 완료되고 아래와 같은 화면을 볼수 있습니다.

Sign in to Github.com 버튼을 클릭하여 github에 로그인을 진행합니다.
로그인이 완료되면 Authorize desktop 버튼을 클릭하여 인증을 진행합니다.

인증버튼을 누르고 나면 웹브라우저가 오픈되면서 Github Desktop을 인증받겠냐는 메시지가 나옵니다. 확인을 눌러 인증을 진행합니다.

continue 버튼을 클릭합니다.

Finish 버튼을 클릭하여 설치를 완료합니다.

Github에 Repository 만들기

http://github.com/계정명 으로 접속하여 Github Repository 에서 동일한 계정명으로 repository를 생성합니다.
사이트에 접속한후 Repositories 탭을 선택한후 New 버튼을 클릭합니다.

 

Repository name 에 계정명을 그대로 입력하고 Create repository 버튼을 클립합니다.

여기까지 진행하면 아래와 같이 완료된 화면을 확인할수 있습니다.

Github Desktop 사용하기

1 Setup in Desktop 버튼을 클릭하여 Github Desktop을 실행합니다. 2 그리고 Clone버튼을 클릭하여 Local Path에 Clone을 진행 합니다.

 

Open in Visual Studio Code를 실햅합니다. (Visual Studio Code는 설치되어 있어야 실행이 가능합니다.)

 

index.html 파일을 하나생성하고 html 이라고 입력하면 팝업이 뜨는데 이중에서 html:5를 선택합니다.

html5를 선택하면 아래와 같은 코드가 자동으로 생성됩니다.

 

아래와 같이 내용을 수정하고 저장 합니다.

열려져 있는 Github Desktop에 아래와 같이 index.html이 추가된것을 알수 있습니다.

 

Description에 어떤 내용이 변경되었는지 적어 주고 Commit to master 버튼을 클릭 합니다.
Push origin 버튼을 클릭하여 작성한 html 문서를 발행합니다.

 

github 사이트에 들어가면 index.html 이 반영되어 있음을 확인할수 있습니다.

아래와 같이 내용을 수정하고 저장하기 버튼을 클릭합니다.

아래 그림과 같이 어떤 내용이 수정되었는지 확인이 가능합니다. 변경된 수정내용을 작성하고 Commit to master 버튼을 클릭 합니다.

Push origin 버튼을 클릭하여 내용을 갱신 합니다.

아래와 같이 github에서 변경된 내용 확인이 가능 합니다.

 

댓글()

Github 계정생성

Setup/opensource|2021. 7. 27. 22:37

Github 계정생성 

소스코드 형상관리를 위한 Github 계정 생성방법

 

사용자 계정 생성

http://www.github.com 접속합니다. 그리고 나서 Sign up for GitHub 버튼을 클릭하여 계정을 생성합니다. 이름 이메일등 필요한 정보를 등록합니다.

 

아래와 같이 항목을 선택하고 complete 버튼을 클릭하면 이메일 인증 메일이 발송됩니다.

 

받은 메일함에서 인증 버튼을 클릭하여 이메일 인증을 진행 합니다.

가입시 등록했던 이메일 주소와 비밀번호를 입력하면 이메일 인증이 완료됩니다. 여기까지 잘 따라 하셨다면 계정생성이 완료되었습니다.

 

댓글()

[AWS 무료 서버구축-18/18] 메모앱 만들기 - frontend vue.js

Setup/aws|2021. 7. 25. 18:15

메모앱 만들기 - frontend vue.js

aws 에 프리티어 서버를 만들고 도메인을 발급받고 jdk 를 설치하고 springboot app 까지 만든이유는 결국 vue.js로 화면을 만들기 위함 입니다. 초간단 메모 app을 만들어 보겠습니다.

화면디자인

만들화면에 대해서 대략적인 화면설계를 해보았습니다.

  • 제목 내용에 해당하는 메모를 검색합니다.
  • 체크박스 체크된 메모를 선택하고 삭제버튼을 클릭하면 체크된 메모를 삭제합니다.
  • 등록 버튼을 클릭하면 우측의 등록 화면의 제목쪽으로 포커스를 이동합니다.
  • 등록 버튼을 클릭하면 메모가 등록되고 좌측에 메모가 하나 추가 됩니다.
  • 페이지 번호를 클릭하면 해당 페이지에 속하는 메모가 리스트업 됩니다.
  • 좌측 메모리스트에서 row를 클릭하면 우측에 제목과 내용이 보이고 수정모드로 변합니다.
  • 수정모드에서 수정 버튼을 클릭하면 메모가 수정되고 수정된 내용이 좌측 리스트에 반영됩니다.

화면개발

위 화면 설계서 되로 아래와 같이 화면을 개발해 보았습니다. 개발하다가 조금더 욕심이 생겨 전체 갯수도 표시하고 한페이지당 보여줄 갯수도 변경했습니다. 등록/검색 버튼을 클릭하여 app 이 제되로 동작하는 확인해 보세요. 참고로 아래 ui 컴포넌트는 element ui를 이용하여 개발 했습니다.

 

backend 서비스 수정

위 화면에서 사용하는 backend 관련 수정파일은 아래와 같습니다.  이전 강좌에서 아래 4개 파일만 수정하시고 수정되면 동일한 요령으로 build 해서 서버에 배포 하고 서비스를 재시작 합니다.

 

. ├── build.gradle └── src ​​​​└── main ​​​​​​​​├── java ​​​​​​​​│ └── io ​​​​​​​​│ └── github ​​​​​​​​│ └── goodsaem ​​​​​​​​│ └── api ​​​​​​​​│ ├── ApiApplication.java ​​​​​​​​│ ├── config ​​​​​​​​│ │ └── WebConfig.java ​​​​​​​​│ ├── controller ​​​​​​​​│ │ └── v1 ​​​​​​​​│ │ └── MemoController.java ​​​​​​​​│ ├── entity ​​​​​​​​│ │ └── Memo.java ​​​​​​​​│ ├── repo ​​​​​​​​│ │ └── MemoJpaRepo.java ​​​​​​​​│ └── service ​​​​​​​​│ ├── IMemoService.java ​​​​​​​​│ └── MemoService.java ​​​​​​​​└── resources ​​​​​​​​​​​​├── application.yml ​​​​​​​​​​​​├── banner.txt ​​​​​​​​​​​​└── templates

MemoController.java

메모 검색 및 페이징 처리를 위해서 아래와 같이 /memo/search 컨트롤러를 추가했습니다. pageable jpa에서 지원해주는 객체로 페이징 처리를 위해 필요한 값 페이지당 보여줄 row 수와 현재페이지 정보를 넘겨주면 알아서 가져올 데이터량을 줄여 줍니다. 즉 query에 offset 과 limit 문장이 추가됩니다.

눈여겨 보아야 되는 부분은 다건 삭제 부분입니다. 삭제 기능은 여러건을 넘겨서 일괄 삭제하는데 pathVariable 로 /spring/v1/memo/1,2,3 이런씩으로 넘기면 이를 List로 받아서 for문을 돌리면서 하나씩 삭제 하는 부분입니다.

package io.github.goodsaem.api.controller.v1; import io.github.goodsaem.api.entity.Memo; import io.github.goodsaem.api.service.IMemoService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; import java.util.List; @RequiredArgsConstructor @RestController @RequestMapping(value = "/v1") @Slf4j public class MemoController { ​​​​private final IMemoService iMemoService; ​​​​@GetMapping(value = "/memo/search") ​​​​public Page<Memo> searchMemo(@RequestParam String keyword, @PageableDefault(size=3,sort="id") Pageable pageable) { ​​​​​​​​return iMemoService.searchMemo(keyword,pageable); ​​​​} ​​​​@GetMapping(value = "/memos") ​​​​public List<Memo> readMemos() { ​​​​​​​​return iMemoService.readMemos(); ​​​​} ​​​​@GetMapping(value = "/memo/{id}") ​​​​public Memo readMemo(@PathVariable long id) { ​​​​​​​​return iMemoService.readMemo(id); ​​​​} ​​​​@PostMapping(value = "/memo") ​​​​public Memo createMemo(@RequestBody Memo memo) { ​​​​​​​​return iMemoService.createMemo(memo.getTitle(), memo.getContents()); ​​​​} ​​​​@PutMapping(value = "/memo") ​​​​public Memo updateMemo(@RequestBody Memo memo) { ​​​​​​​​return iMemoService.updateMemo(memo.getId(), memo.getTitle(), memo.getContents()); ​​​​} ​​​​@DeleteMapping(value = "/memo/{id}") ​​​​public void deleteMemo(@PathVariable long id) { ​​​​​​​​iMemoService.deleteMemo(id); ​​​​} ​​​​@DeleteMapping(value = "/memos/{ids}") ​​​​public void deleteMemos(@PathVariable("ids") List<Long> ids) { ​​​​​​​​for(Long id:ids) { ​​​​​​​​​​​​iMemoService.deleteMemo(id); ​​​​​​​​} ​​​​} }

 

MemoJpaRepo.java

검색조건을 가져오는 부분을 아래와 같이 @Query를 통해서 작성해 보았습니다. 이렇게 진행하지 않고 세련되게 작성하는 방법도 있을것 같은데 아직 jpa에 익숙하지 않아 내공이 쌓이면 따로 정리하겠습니다. 화면에서 검색어 keyword가 전달되면 제목 및 내용에서 찾는 간단한 sql 입니다. 정렬은 가장 마지막에 등록한 메모가 보이게 했구요 countQuery도 같이 작성하여 페이징 처리시 참고 합니다.

package io.github.goodsaem.api.repo; import io.github.goodsaem.api.entity.Memo; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; public interface MemoJpaRepo extends JpaRepository<Memo, Long> { ​​​​Page<Memo> findAll(Pageable pageable); ​​​​@Query( ​​​​​​​​value = "SELECT m FROM Memo m WHERE m.title LIKE %:keyword% OR m.contents LIKE %:keyword% ORDER BY m.id DESC", ​​​​​​​​countQuery = "SELECT COUNT(m.id) FROM Memo m WHERE m.title LIKE %:keyword% OR m.contents LIKE %:keyword%" ​​​​) ​​​​Page<Memo> findAllSearch(String keyword,Pageable pageable); }

 

IMemoService.java

메모 검색을 사용하기 위한 searchMemo 메소드를 추가했습니다.

package io.github.goodsaem.api.service; import io.github.goodsaem.api.entity.Memo; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import java.util.List; public interface IMemoService { ​​​​List<Memo> readMemos(); ​​​​Page<Memo> searchMemo(String keyword, Pageable pageable); ​​​​Memo readMemo(long id); ​​​​Memo createMemo(String title,String contents); ​​​​Memo updateMemo(long id,String title,String contents); ​​​​void deleteMemo(long id); }

 

MemoService.java

메모 서비스에서 메모를 검색하는 부분을 구현했습니다.

package io.github.goodsaem.api.service; import io.github.goodsaem.api.entity.Memo; import io.github.goodsaem.api.repo.MemoJpaRepo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.List; @Slf4j @Service @RequiredArgsConstructor public class MemoService implements IMemoService { ​​​​private final MemoJpaRepo memoJpaRepo; ​​​​@Override ​​​​public List<Memo> readMemos() { ​​​​​​​​return memoJpaRepo.findAll(); ​​​​} ​​​​@Override ​​​​public Page<Memo> searchMemo(String keyword, Pageable pageable) { ​​​​​​​​Page<Memo> memo = memoJpaRepo.findAllSearch(keyword,pageable); ​​​​​​​​return memo; ​​​​} ​​​​@Override ​​​​public Memo readMemo(long id) { ​​​​​​​​return memoJpaRepo.findById(id).orElse(null); ​​​​} ​​​​@Override ​​​​public Memo createMemo(String title, String contents) { ​​​​​​​​Memo memo = Memo.builder() ​​​​​​​​​​​​​​​​.title(title) ​​​​​​​​​​​​​​​​.contents(contents) ​​​​​​​​​​​​​​​​.build(); ​​​​​​​​return memoJpaRepo.save(memo); ​​​​} ​​​​@Override ​​​​public Memo updateMemo(long id, String title, String contents) { ​​​​​​​​Memo memo = Memo.builder() ​​​​​​​​​​​​​​​​.id(id) ​​​​​​​​​​​​​​​​.title(title) ​​​​​​​​​​​​​​​​.contents(contents) ​​​​​​​​​​​​​​​​.build(); ​​​​​​​​return memoJpaRepo.save(memo); ​​​​} ​​​​@Override ​​​​public void deleteMemo(long id) { ​​​​​​​​memoJpaRepo.deleteById(id); ​​​​} }

 

FrontEnd 소스 상세설명

 

MemoCreate.vue

위 화면중 우측 부분 내용으로 메모를 등록하는 폼은 다른 화면에서 사용할수 있을것 같아 별도의 컴포넌트로 분리했습니다. MemoCreate를 호출하는 부모 컨포넌트는 memo 모델을 넘겨줍니다. 그럼 memo 모델의 값을 복제해서 화면에서 활용하고 데이터 수정이 끝나고 저장 버튼을 누르면 상위 컴포넌트에게 save 되었다고 알려(emit) 줍니다. 자세한 내용은 vue 강좌 때 다시 설명하겠습니다.

<template> ​​<div style="margin-top: 10px"> ​​​​<el-row> ​​​​​​<el-col :span="4" align="center"> ​​​​​​​​제목 ​​​​​​</el-col> ​​​​​​<el-col :span="20"> ​​​​​​​​<el-input ref="title" placeholder="메모 제목을 입력하세요" v-model="memo.title"></el-input> ​​​​​​</el-col> ​​​​</el-row> ​​​​<el-row style="margin-top: 10px"> ​​​​​​<el-col :span="4" align="center"> ​​​​​​​​내용 ​​​​​​</el-col> ​​​​​​<el-col :span="20"> ​​​​​​​​<el-input ​​​​​​​​​​​​type="textarea" ​​​​​​​​​​​​:rows="7" ​​​​​​​​​​​​placeholder="Please input" ​​​​​​​​​​​​v-model="memo.contents"> ​​​​​​​​</el-input> ​​​​​​</el-col> ​​​​</el-row> ​​​​<el-row style="margin-top: 10px"> ​​​​​​<el-col :span="24" align="center"> ​​​​​​​​<el-button type="primary" @click="saveMemo">저장</el-button> ​​​​​​</el-col> ​​​​</el-row> ​​</div> </template> <script> export default { ​​name: "MemoCreate", ​​props : { ​​​​model : { ​​​​​​type : Object, ​​​​} ​​}, ​​created() { ​​}, ​​data() { ​​​​return { ​​​​​​memo : { ​​​​​​​​id : -1, ​​​​​​​​title : '', ​​​​​​​​contents : '', ​​​​​​}, ​​​​} ​​}, ​​methods: { ​​​​create() { ​​​​}, ​​​​saveMemo() { ​​​​​​this.$emit("save",this.memo) ​​​​} ​​}, ​​watch: { ​​​​model(model) { ​​​​​​this.memo = Object.assign({},model); ​​​​​​this.$refs.title.focus(); ​​​​} ​​} } </script> <style scoped></style>

Exam5.vue

검색 및 결과를 출력하는 컴포넌트 입니다. 화면을 그리는 부분 빼고 실제 코딩은 많지 않습니다. 

<template> ​​<div style="margin-top: 10px"> ​​​​<el-row> ​​​​​​<el-col :span="12"> ​​​​​​​​<el-row> ​​​​​​​​​​<el-col :span="4" style="padding-right: 10px;"> ​​​​​​​​​​​​검색어 : ​​​​​​​​​​</el-col> ​​​​​​​​​​<el-col :span="12" style="padding-right: 10px;"> ​​​​​​​​​​​​<el-input @change="searchMemo" v-model="keyword"></el-input> ​​​​​​​​​​</el-col> ​​​​​​​​​​<el-col :span="8" style="padding-left: 10px;"> ​​​​​​​​​​​​<el-button type="primary" @click="searchMemo">메모검색</el-button> ​​​​​​​​​​</el-col> ​​​​​​​​</el-row> ​​​​​​​​<el-row style="margin-top: 10px"> ​​​​​​​​​​<el-col> ​​​​​​​​​​​​전체 : {{ total }} ​​​​​​​​​​​​<el-button type="primary" @click="deleteMemo">삭제</el-button> ​​​​​​​​​​​​<el-button type="primary" @click="createMemo">등록</el-button> ​​​​​​​​​​​​<el-select v-model="size" ​​​​​​​​​​​​​​​​​​​​​​​placeholder="한페이지당 row수" ​​​​​​​​​​​​​​​​​​​​​​​@change="searchMemo" ​​​​​​​​​​​​> ​​​​​​​​​​​​​​<el-option ​​​​​​​​​​​​​​​​​​v-for="(row,index) in 100" ​​​​​​​​​​​​​​​​​​:key="index" ​​​​​​​​​​​​​​​​​​:label="row" ​​​​​​​​​​​​​​​​​​:value="row"> ​​​​​​​​​​​​​​</el-option> ​​​​​​​​​​​​</el-select> ​​​​​​​​​​</el-col> ​​​​​​​​</el-row> ​​​​​​​​<el-row style="margin-top: 10px"> ​​​​​​​​​​<el-col> ​​​​​​​​​​​​<el-row> ​​​​​​​​​​​​​​<el-table ​​​​​​​​​​​​​​​​​​ref="table" ​​​​​​​​​​​​​​​​​​v-if="gridData.length >= 0" ​​​​​​​​​​​​​​​​​​@row-click="rowClick" ​​​​​​​​​​​​​​​​​​:data="gridData" ​​​​​​​​​​​​​​​​​​style="width: 100%"> ​​​​​​​​​​​​​​​​<el-table-column ​​​​​​​​​​​​​​​​​​​​type="selection" ​​​​​​​​​​​​​​​​​​​​align="center" ​​​​​​​​​​​​​​​​​​​​width="55"> ​​​​​​​​​​​​​​​​</el-table-column> ​​​​​​​​​​​​​​​​<el-table-column ​​​​​​​​​​​​​​​​​​​​prop="id" ​​​​​​​​​​​​​​​​​​​​align="center" ​​​​​​​​​​​​​​​​​​​​label="번호" ​​​​​​​​​​​​​​​​​​​​width="50"> ​​​​​​​​​​​​​​​​</el-table-column> ​​​​​​​​​​​​​​​​<el-table-column ​​​​​​​​​​​​​​​​​​​​prop="title" ​​​​​​​​​​​​​​​​​​​​label="제목" ​​​​​​​​​​​​​​​​​​​​header-align="center" ​​​​​​​​​​​​​​​​​​​​style="width: 95%"> ​​​​​​​​​​​​​​​​</el-table-column> ​​​​​​​​​​​​​​</el-table> ​​​​​​​​​​​​</el-row> ​​​​​​​​​​​​<el-row style="margin-top: 10px"> ​​​​​​​​​​​​​​<el-col> ​​​​​​​​​​​​​​​​<el-pagination ​​​​​​​​​​​​​​​​​​​​background ​​​​​​​​​​​​​​​​​​​​layout="prev, pager, next" ​​​​​​​​​​​​​​​​​​​​@current-change="chgPage" ​​​​​​​​​​​​​​​​​​​​:current-page="page+1" ​​​​​​​​​​​​​​​​​​​​:page-size="size" ​​​​​​​​​​​​​​​​​​​​:total="total"> ​​​​​​​​​​​​​​​​</el-pagination> ​​​​​​​​​​​​​​</el-col> ​​​​​​​​​​​​</el-row> ​​​​​​​​​​</el-col> ​​​​​​​​</el-row> ​​​​​​</el-col> ​​​​​​<el-col :span="12" style="border-left: 1px solid #efefef;"> ​​​​​​​​<memo-create :model="memo" @save="saveMemo"/> ​​​​​​</el-col> ​​​​</el-row> ​​</div> </template> <script> const URL = "https://goodsaem.ml/spring"; //const URL = "http://localhost:9090/spring"; import MemoCreate from "./MemoCreate"; export default { ​​name: "Exam5", ​​created() { ​​}, ​​components: { ​​​​MemoCreate ​​}, ​​data() { ​​​​return { ​​​​​​page: 0, ​​​​​​size : 3, ​​​​​​total : 0, ​​​​​​keyword: '', ​​​​​​memo: { ​​​​​​​​title: '', ​​​​​​​​contents: '', ​​​​​​}, ​​​​​​gridData: [], ​​​​} ​​}, ​​methods: { ​​​​create() { ​​​​}, ​​​​rowClick(v) { ​​​​​​this.memo = v; ​​​​}, ​​​​chgPage(page) { ​​​​​​this.page=page-1 ​​​​​​this.searchMemo(); ​​​​}, ​​​​saveMemo(memo) { ​​​​​​if (memo.id === -1) { ​​​​​​​​this.$http.post(URL + '/v1/memo', memo).then((response) => { ​​​​​​​​​​this.searchMemo(); ​​​​​​​​}) ​​​​​​} else { ​​​​​​​​this.$http.put(URL + '/v1/memo', memo).then((response) => { ​​​​​​​​​​this.searchMemo(); ​​​​​​​​}) ​​​​​​} ​​​​}, ​​​​createMemo() { ​​​​​​this.memo = Object.assign({ ​​​​​​​​id : -1, ​​​​​​​​title : '', ​​​​​​​​contents : '', ​​​​​​}) ​​​​}, ​​​​deleteMemo() { ​​​​​​let ids = ""; ​​​​​​this.$refs.table.selection.map(r => ids += r.id + ","); ​​​​​​ids = ids.substring(0,ids.length-1); ​​​​​​if(this.$refs.table.selection.length > 0) { ​​​​​​​​this.$http.delete(URL + '/v1/memos/' + ids) ​​​​​​​​​​​​.then((response) => { ​​​​​​​​​​​​​​this.$message("정상적으로 삭제되었습니다."); ​​​​​​​​​​​​​​this.searchMemo(); ​​​​​​​​​​​​}) ​​​​​​} ​​​​}, ​​​​searchMemo() { ​​​​​​this.$http.get(URL + '/v1/memo/search', { ​​​​​​​​params: { ​​​​​​​​​​keyword : this.keyword, ​​​​​​​​​​page: this.page, ​​​​​​​​​​size: this.size, ​​​​​​​​} ​​​​​​}) ​​​​​​.then((response) => { ​​​​​​​​console.log(response) ​​​​​​​​this.gridData = response.data.data.content; ​​​​​​​​this.total = response.data.totalElements; ​​​​​​}) ​​​​} ​​} } </script> <style scoped></style>

마치며

AWS에 프리티어 서버를 셋업하고 spring boot 로 백엔드를 만들고 fontend 는 vue.js 를통해서 앱을 만들어 보았습니다.

긴글 읽어 주셔서 감사합니다.

댓글()

[AWS 무료 서버구축-17/18] 메모앱 만들기 - backend 서비스 만들기

Setup/aws|2021. 7. 25. 17:31

메모앱 만들기

지금까지 구축한 aws 서버위에 간단한 메모 application을 만들어서 배포 하겠습니다

 

테이블 생성 및 초기데이터 적재

테이블을 생성하고 초기 데이터를 인서트 합니다.

아래 스크립트는 aws 의 mariadb 에서 실행합니다.

먼저 메모 테이블을 생성합니다.

CREATE TABLE `memo` ( ​​`id` bigint(20) NOT NULL, ​​`contents` varchar(4000) NOT NULL, ​​`title` varchar(200) NOT NULL, ​​PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

자동증가 시퀀스 테이블을 만듭니다.

CREATE TABLE `hibernate_sequence` ( ​​`next_val` bigint(20) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

초기 데이터를 생성합니다.

INSERT INTO memo (id, contents, title) VALUES(1, '내용1', '제목1'); INSERT INTO memo (id, contents, title) VALUES(2, '내용2', '제목2'); INSERT INTO memo (id, contents, title) VALUES(3, '내용3', '제목3'); INSERT INTO hibernate_sequence (next_val) VALUES(4);

 

앱구조

springboot application 구조는 아래와 같습니다.

. ├── build.gradle └── src ​​​​└── main ​​​​​​​​├── java ​​​​​​​​│ └── io ​​​​​​​​│ └── github ​​​​​​​​│ └── goodsaem ​​​​​​​​│ └── api ​​​​​​​​│ ├── ApiApplication.java ​​​​​​​​│ ├── config ​​​​​​​​│ │ └── WebConfig.java ​​​​​​​​│ ├── controller ​​​​​​​​│ │ └── v1 ​​​​​​​​│ │ └── MemoController.java ​​​​​​​​│ ├── entity ​​​​​​​​│ │ └── Memo.java ​​​​​​​​│ ├── repo ​​​​​​​​│ │ └── MemoJpaRepo.java ​​​​​​​​│ └── service ​​​​​​​​│ ├── IMemoService.java ​​​​​​​​│ └── MemoService.java ​​​​​​​​└── resources ​​​​​​​​​​​​├── application.yml ​​​​​​​​​​​​├── banner.txt ​​​​​​​​​​​​└── templates

 

build.gradle

특별한 내용은 없고 springboot 2.4.4 버전 사용 및 mariadb 와 jpa 라이브러리 추가및 lombok 플러그인을 추가했습니다.

buildscript { ‌ext { ‌‌springBootVersion = '2.4.4' } ‌repositories { ‌‌mavenCentral() } ‌dependencies { ‌‌classpath( ‌‌‌‌"org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'org.springframework.boot' apply plugin: 'war' apply plugin: 'io.spring.dependency-management' sourceCompatibility = '1.8' targetCompatibility = '1.8' group = 'io.github.goodsaem' version = '1.1' configurations { ‌compileOnly { ‌‌extendsFrom annotationProcessor } } repositories { ‌mavenCentral() } dependencies { ‌implementation 'org.springframework.boot:spring-boot-starter-data-jpa' ‌implementation 'org.springframework.boot:spring-boot-starter-web' ‌implementation 'org.springframework.boot:spring-boot-autoconfigure' ‌implementation("org.mariadb.jdbc:mariadb-java-client") ‌compileOnly 'org.projectlombok:lombok' ‌annotationProcessor 'org.projectlombok:lombok' ‌testImplementation 'org.springframework.boot:spring-boot-starter-test' } test { ‌useJUnitPlatform() }

 

ApiApplication.java

springboot 시작 파일 입니다.

package io.github.goodsaem.api; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ApiApplication { public static void main(String[] args) { ‌‌SpringApplication.run(ApiApplication.class, args); } }

 

WebConfig.java

CORS 설정 파일입니다. 허용할 도메인에 대해서 아래와 같이 정의 합니다.

package io.github.goodsaem.api.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { ​​​​@Override ​​​​public void addCorsMappings(CorsRegistry registry) { ​​​​​​​​registry.addMapping("/**") ​​​​​​​​​​​​​​​​.allowedOrigins("https://goodsaem.github.io","http://localhost:8080") ​​​​​​​​​​​​​​​​.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE","OPTIONS"); ​​​​} }

 

MemoController.java

restapi controller 입니다. 메모의 crud url 을 호출하면 해당 요청에 맞는 서비스를 호출하여 db 작업을 수행합니다

package io.github.goodsaem.api.controller.v1; import io.github.goodsaem.api.entity.Memo; import io.github.goodsaem.api.service.IMemoService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.List; @RequiredArgsConstructor @RestController @RequestMapping(value = "/v1") public class MemoController { ​​​​private final IMemoService iMemoService; ​​​​@GetMapping(value = "/memos") ​​​​public List<Memo> readMemos() { ​​​​​​​​return iMemoService.readMemos(); ​​​​} ​​​​@GetMapping(value = "/memo/{id}") ​​​​public Memo readMemo( @PathVariable long id) { ​​​​​​​​return iMemoService.readMemo(id); ​​​​} ​​​​@PostMapping(value = "/memo") ​​​​public Memo createMemo(@RequestParam String title,String contents) { ​​​​​​​​return iMemoService.createMemo(title,contents); ​​​​} ​​​​@PutMapping(value = "/memo") ​​​​public Memo updateMemo(@RequestParam long id, @RequestParam String title,String contents) { ​​​​​​​​return iMemoService.updateMemo(id,title,contents); ​​​​} ​​​​@DeleteMapping(value="/memo/{id}") ​​​​public void deleteMemo(@PathVariable long id) { ​​​​​​​​iMemoService.deleteMemo(id); ​​​​} ​​​​@DeleteMapping(value="/memos") ​​​​public void deleteMemos() { ​​​​​​​​iMemoService.deleteMemos(); ​​​​} }

 

Memo.java

memo entity 입니다.

package io.github.goodsaem.api.entity; import lombok.*; import javax.persistence.*; @Builder @Entity @Setter @Getter @NoArgsConstructor @AllArgsConstructor @Table(name="memo") public class Memo { ​​​​@Id ​​​​@GeneratedValue(strategy = GenerationType.AUTO) ​​​​private long id; ​​​​@Column(nullable = false, length = 30) ​​​​private String title; ​​​​@Column(nullable = false, length = 4000) ​​​​private String contents; }

 

MemoJpaRepo.java

jpa를 사용하기 위해 jparepository를 상속 받습니다.

package io.github.goodsaem.api.repo; import io.github.goodsaem.api.entity.Memo; import org.springframework.data.jpa.repository.JpaRepository; public interface MemoJpaRepo extends JpaRepository<Memo, Long> { }

 

IMemoService.java

서비스 인터페이스 입니다.

package io.github.goodsaem.api.service; import io.github.goodsaem.api.entity.Memo; import java.util.List; public interface IMemoService { ​​​​List<Memo> readMemos(); ​​​​Memo readMemo(long id); ​​​​Memo createMemo(String title,String contents); ​​​​Memo updateMemo(long id,String title,String contents); ​​​​void deleteMemo(long id); ​​​​void deleteMemos(); }

 

MemoService.java

jpaRepo로 실제 crud 작업을 수행합니다.

package io.github.goodsaem.api.service; import io.github.goodsaem.api.entity.Memo; import io.github.goodsaem.api.repo.MemoJpaRepo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; @Slf4j @Service @RequiredArgsConstructor public class MemoService implements IMemoService { ​​​​private final MemoJpaRepo memoJpaRepo; ​​​​@Override ​​​​public List<Memo> readMemos() { ​​​​​​​​return memoJpaRepo.findAll(); ​​​​} ​​​​@Override ​​​​public Memo readMemo(long id) { ​​​​​​​​return memoJpaRepo.findById(id).orElse(null); ​​​​} ​​​​@Override ​​​​public Memo createMemo(String title, String contents) { ​​​​​​​​Memo memo = Memo.builder() ​​​​​​​​​​​​​​​​.title(title) ​​​​​​​​​​​​​​​​.contents(contents) ​​​​​​​​​​​​​​​​.build(); ​​​​​​​​return memoJpaRepo.save(memo); ​​​​} ​​​​@Override ​​​​public Memo updateMemo(long id, String title, String contents) { ​​​​​​​​Memo memo = Memo.builder() ​​​​​​​​​​​​​​​​.id(id) ​​​​​​​​​​​​​​​​.title(title) ​​​​​​​​​​​​​​​​.contents(contents) ​​​​​​​​​​​​​​​​.build(); ​​​​​​​​return memoJpaRepo.save(memo); ​​​​} ​​​​@Override ​​​​public void deleteMemo(long id) { ​​​​​​​​memoJpaRepo.deleteById(id); ​​​​} ​​​​@Override ​​​​public void deleteMemos() { ​​​​​​​​memoJpaRepo.deleteAll(); ​​​​} }

 

application.yml

appplication 설정 내용으로 mariadb 연결정보와 서비스 포트 context path 설정정보를 가지고 있습니다.

server: ​​port: 9090 ​​servlet: ​​​​context-path: /spring ​​​​session: ​​​​​​timeout: 60 ​​​​​​tomcat: ​​​​​​​​uri-encoding: UTF-8 spring: ​​datasource: ​​​​driver-class-name: org.mariadb.jdbc.Driver ​​​​url: jdbc:mariadb://localhost:3306/goodsaem?useSSL=false&serverTimezone=KST ​​​​username: goodsaem ​​​​password: xxxxxxxxxx ​​jpa: ​​​​database-platform: org.hibernate.dialect.MariaDBDialect ​​​​properties.hibernate.hbm2ddl.auto: none ​​​​showSql: true

 

banner.txt

spring boot 시작시 사용할 배너 입니다.

※ 배너는 http://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20 여기에서 만들수 있습니다.

​██████╗ ██████╗ ██████╗ ██████╗ ███████╗ █████╗ ███████╗███╗ ███╗ ██╔════╝ ██╔═══██╗██╔═══██╗██╔══██╗██╔════╝██╔══██╗██╔════╝████╗ ████║ ██║ ███╗██║ ██║██║ ██║██║ ██║███████╗███████║█████╗ ██╔████╔██║ ██║ ██║██║ ██║██║ ██║██║ ██║╚════██║██╔══██║██╔══╝ ██║╚██╔╝██║ ╚██████╔╝╚██████╔╝╚██████╔╝██████╔╝███████║██║ ██║███████╗██║ ╚═╝ ██║ ​╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ :: Spring Boot :: ver : ${spring-boot.version}

 

api-1.1.war 파일 만들기

프로젝트 시작 home 디렉토리에서 아래 명령어를 입력하여 war 파일을 만듭니다.

sh gradlew build

 

war file 업로드

aws 서버에 api-1.1.war 파일을 업로드 합니다.

 

springboot 재시작

aws 서버에 접속한후 아래 명령어를 입력합니다.

서비스 중지

ubuntu@goodsaem:~$ ./stop.sh =====spring is running at 5808 Shutdown spring now

 

서비스 시작

ubuntu@goodsaem:~$ ./start.sh;./log.sh

 

시작 로그

​██████╗ ██████╗ ██████╗ ██████╗ ███████╗ █████╗ ███████╗███╗ ███╗ ██╔════╝ ██╔═══██╗██╔═══██╗██╔══██╗██╔════╝██╔══██╗██╔════╝████╗ ████║ ██║ ███╗██║ ██║██║ ██║██║ ██║███████╗███████║█████╗ ██╔████╔██║ ██║ ██║██║ ██║██║ ██║██║ ██║╚════██║██╔══██║██╔══╝ ██║╚██╔╝██║ ╚██████╔╝╚██████╔╝╚██████╔╝██████╔╝███████║██║ ██║███████╗██║ ╚═╝ ██║ ​╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ :: Spring Boot :: ver : 2.4.4 2021-03-27 17:10:46.869 INFO 7734 --- [ main] io.github.goodsaem.api.ApiApplication : Starting ApiApplication using Java 1.8.0_282 on goodsaem with PID 7734 (/home/ubuntu/api-1.1.war started by ubuntu in /home/ubuntu) 2021-03-27 17:10:46.874 INFO 7734 --- [ main] io.github.goodsaem.api.ApiApplication : No active profile set, falling back to default profiles: default 2021-03-27 17:10:48.714 INFO 7734 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2021-03-27 17:10:48.852 INFO 7734 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 121 ms. Found 1 JPA repository interfaces. 2021-03-27 17:10:50.415 INFO 7734 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 9090 (http) 2021-03-27 17:10:50.442 INFO 7734 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-03-27 17:10:50.446 INFO 7734 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.44] 2021-03-27 17:10:51.931 INFO 7734 --- [ main] o.a.c.c.C.[.[localhost].[/spring] : Initializing Spring embedded WebApplicationContext 2021-03-27 17:10:51.932 INFO 7734 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 4908 ms 2021-03-27 17:10:52.874 INFO 7734 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2021-03-27 17:10:52.986 INFO 7734 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.29.Final 2021-03-27 17:10:53.296 INFO 7734 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final} 2021-03-27 17:10:53.661 INFO 7734 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2021-03-27 17:10:53.804 INFO 7734 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2021-03-27 17:10:53.848 INFO 7734 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MariaDBDialect 2021-03-27 17:10:54.767 INFO 7734 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform] 2021-03-27 17:10:54.779 INFO 7734 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2021-03-27 17:10:55.548 WARN 7734 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning 2021-03-27 17:10:55.822 INFO 7734 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-03-27 17:10:56.395 INFO 7734 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9090 (http) with context path '/spring' 2021-03-27 17:10:56.413 INFO 7734 --- [ main] io.github.goodsaem.api.ApiApplication : Started ApiApplication in 10.795 seconds (JVM running for 11.987)

 

웹브라우저에서 확인

https://goodsaem.ml/spring/v1/memosurl 접근했을때 아래와 같은 문자열이 출력되면 정상입니다.

[ ​​{ ​​​​"id": 1, ​​​​"title": "제목1", ​​​​"contents": "내용1" ​​}, ​​{ ​​​​"id": 2, ​​​​"title": "제목2", ​​​​"contents": "내용2" ​​}, ​​{ ​​​​"id": 3, ​​​​"title": "제목3", ​​​​"contents": "내용3" ​​} ]

 

전체소스 다운로드

지금까지 만든 소스는 아래 링크를 통해서 다운로드 가능합니다.

api2.zip
0.13MB

댓글()

[AWS 무료 서버구축-16/18] SpringBoot 백그라운드에서 실행하기

Setup/aws|2021. 7. 25. 16:50

SpringBoot 백그라운드에서 실행하기

아래 명령어로 샘플 프로젝트를 실행하면 foreground 로 명령어가 실행됩니다. ctrl + c 키를 눌러서 종료하면 프로세스가 종료됩니다. 명령어를 실행하고 나서 로그아웃을 하더라도 명령어가 계속 실행된 상태로 있을려면 background 로 명령어가 실행되어야 합니다. 

java -jar hello-0.0.1-SNAPSHOT.war

background로 실행하는 프로세스를 만들기 위해 아래와 같이 start.sh 파일을 생성합니다.

ubuntu@goodsaem:~$ vi start.sh

nohup을 사용하여 출력을 nohup.out으로 보내고 명령어를 실행한다음 & 같이 입력하여 백그라운드에서 실행 되게 만듭니다.

nohup java -jar hello*.war &

그리고 나서 start.sh 실행 권한을 부여 합니다.

ubuntu@goodsaem:~$ chmod +x start.sh

stop 스크립트를 작성합니다.

ubuntu@goodsaem:~$ vi stop.sh

아래와 같이 입력합니다. 프로세스를 찾아서 kill 하는 명령어 입니다.

#!/bin/sh PID=`ps -ef | grep java | grep war | awk '{print $2}'` if [ -n "$PID" ] then ​​echo "=====spring is running at" $PID "Shutdown spring now" ​​kill -9 $PID else ​​echo "=====spring isn't running=====" fi

저장하고 종료합니다. log 출력 스크립트를 작성합니다.

ubuntu@goodsaem:~$ vi log.sh

아래와 같이 입력합니다

tail -f nohup.out

stop.sh 와 log.sh 각각 실행권한을 부여합니다.

ubuntu@goodsaem:~$ chmod +X start.sh ubuntu@goodsaem:~$ chmod +X log.sh

서비스를 시작합니다.

ubuntu@goodsaem:~$ ./start.sh;./log.sh

아래와 같은 로그 확인이 가능합니다.

​​. ____ _ __ _ _ ​/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ ​\\/ ___)| |_)| | | | | || (_| | ) ) ) ) ​​' |____| .__|_| |_|_| |_\__, | / / / / ​=========|_|==============|___/=/_/_/_/ ​:: Spring Boot :: (v2.4.4) 2021-03-27 02:23:27.115 INFO 3532 --- [ main] g.github.io.hello.HelloApplication : Starting HelloApplication using Java 1.8.0_282 on goodsaem with PID 3532 (/home/ubuntu/hello-0.0.1-SNAPSHOT.war started by ubuntu in /home/ubuntu) 2021-03-27 02:23:27.124 INFO 3532 --- [ main] g.github.io.hello.HelloApplication : No active profile set, falling back to default profiles: default 2021-03-27 02:23:29.570 INFO 3532 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 9090 (http) 2021-03-27 02:23:29.600 INFO 3532 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-03-27 02:23:29.606 INFO 3532 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.44] 2021-03-27 02:23:30.416 INFO 3532 --- [ main] o.a.c.c.C.[.[localhost].[/spring] : Initializing Spring embedded WebApplicationContext 2021-03-27 02:23:30.416 INFO 3532 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3156 ms 2021-03-27 02:23:31.421 INFO 3532 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-03-27 02:23:31.886 INFO 3532 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9090 (http) with context path '/spring' 2021-03-27 02:23:31.908 INFO 3532 --- [ main] g.github.io.hello.HelloApplication : Started HelloApplication in 6.02 seconds (JVM running for 6.993)

이제 ctrl + c 를 해도 프로세스가 종료되지 않습니다.

 

 

서비스 종료

아래 명령어로 서비스를 종료 합니다.

ubuntu@goodsaem:~$ ./stop.sh;./log.sh =====spring is running at 3532 Shutdown spring now

다시 서비스를 시작합니다.

ubuntu@goodsaem:~$ ./start.sh;./log.sh

 

테스트

이제 실제로 https 로 접속을 시도해 보겠습니다. http://goodsaem.ml/spring/goodsaem/string (opens new window) https 프로토콜을 이용하여 설정한 도메인으로 접속하니 아래 그림과 같이 정상적으로 화면이 나왔습니다.

 

댓글()

[AWS 무료 서버구축-15/18] SSL 사이트 점검하기

Setup/aws|2021. 7. 25. 16:35

SSL 사이트 점검하기 

SSL이 제되로 생성되었는지 https://www.ssllabs.com/ssltest/analyze.html사이트에 접속하여 아래와 같이 도메인을 입력하고 점검을 수행합니다.

A 라고  표시되면 설정 상태가 정상입니다.

댓글()