for file in `ls *.gz`; do gunzip "${file}"
-d "${file:0:-4}"; done
for file in `ls *.zip`; do unzip "${file}"; done
2019.07.17
Python 을 통한 간단한 Web API 구축: Django REST Framework2018.08.28
가비아 도메인과 구글앱엔진 연동 22018.08.03
우분투 그룹 관련 명령어 정리2018.02.15
리눅스 파일 이름으로 하위 디렉토리의 파일 삭제2018.02.10
리눅스 현재 폴더 내 모든 zip 파일 압축 풀기2017.11.13
나눔바른고딕, 나눔고딕 CSS 적용하기2017.02.13
Google App Engine - standard vs flexible 22017.02.05
Google App Engine - Error: Could not load the default credentials. 12017.02.02
Aspect Oriented Programming(관점지향프로그래밍) 소개 42017.01.24
XPress Engine 설치 방법Django REST Framework
최근 tensorflow/keras 기반의 파이썬 어플리케이션을 코드를 공개하지 않으면서 서비스하기 위한 방향을 고민하던 도중, Web API 를 이용하면 좋을 것이라는 생각이 들었다. 찾아보니 Django REST Framework 라고 하는 Django 기반 프레임워크를 통해 이러한 Web API 를 간단하게 구현할 수 있는 것 같다.
Django REST Framework 는 Django 기반의 Web API 개발을 위한 Framework이다. 홈페이지에 있는 튜토리얼을 따라하면 다음과 같이 간단한 정보를 요청하고 받아오는 간단한 샘플 어플리케이션을 만들어볼 수 있다.
나눔바른고딕, 나눔고딕 CSS 적용하기 (0) | 2017.11.13 |
---|---|
Aspect Oriented Programming(관점지향프로그래밍) 소개 (4) | 2017.02.02 |
가비아 도메인과 구글앱엔진 연동
구글앱엔진으로 구축한 웹 어플리케이션은 따로 설정하지 않으면 [프로젝트명].appspot.com으로 웹에서 접근할 수 있게 됩니다. 하지만 맞춤 도메인을 통해 개인이 소유한 도메인을 연결시킬 수 있습니다. 예를 들어, 가비아에서 도메인을 구입한 뒤 구글앱엔진으로 구축한 웹 애플리케이션에 연결시킬 수 있습니다. 가장 먼저 할 일은 물론 가비아에서 도메인을 구입하여야 하는 것입니다. 가비아에서 도메인을 구입하였고, 구글앱엔진으로 구축하여 appspot으로 돌아가는 웹 애플리케이션이 있다는 가정하에 아래 과정을 진행하면 됩니다.
구글앱엔진 설정
왼쪽 대시보드 설정 - 맞춤 도메인 - 새 맞춤 도메인 추가
그러면 이렇게 해당 도메인의 DNS 구성에 TXT 기록을 추가하라고 나옵니다. 이 화면에서 멈춰놓고 가비아 홈페이지에 접속하여 이 기록을 추가하여야합니다.
가비아 설정
가비아 홈페이지 - DNS 설정 - 레코드 추가
여기서 호스트에는 @를 하고 값/위치에 위에 구글에서 알려준 TXT 기록을 입력한 후 저장을 누릅니다. (호스트에 반드시 @를 쓰셔야합니다. 다른 걸 쓰면 안됩니다..)
다음에 구글앱엔진으로 돌아와 확인을 누르면 도메인에 대한 소유권이 확인됩니다. 여기까지 되었으면 해당 도메인이 본인 거라는 것을 구글이 확인한 것입니다. 이제 그 도메인으로 접속할 수 있으려면 A레코드를 추가해야하는데 위 과정을 진행하면 앱엔진 대시보드 상에 다음과 화면이 나오게 됩니다. 여기에서 "데이터" 란에 표시된 IP 주소를 가비아에 입력해주어야합니다.
아까의 가비아 DNS 설정 화면으로 돌아와서 레코드 추가에서 타입을 A, 호스트는 @그리고 값에는 위의 IP 주소를 넣으면 설정이 완료됩니다. 그러면 해당 도메인으로 구글 앱 엔진에 접속할 수 있게 됩니다.
하위 도메인 설정 방법
구글 앱엔진에서 하위 도메인(www를 예로 들음)을 추가한 후에, 가비아에서 1. 레코드 추가-타입 CNAME 2. 호스트 - www, 3. 값 - 구글앱엔진 대시보드 상의 "데이터" (저의 경우에는 ghs.googlehosted.com 을 입력하였습니다.) 를 입력한 후 저장하면 www.domain.com 으로도 구글 웹앤진을 접속할 수 있습니다.
Google App Engine - standard vs flexible (2) | 2017.02.13 |
---|---|
Google App Engine - Error: Could not load the default credentials. (1) | 2017.02.05 |
구글 클라우드 SDK로 Python 웹 애플리케이션 구축하기 (2) | 2016.10.29 |
우분투 그룹 관련 명령어 정리
우분투를 다중 사용자 환경에서 이용하다보면 특정 폴더를 특정 그룹만 접근할 수 있게 하고 싶을 때가 있습니다.
이럴 때 유용한 그룹 관련 명령어를 정리해보려고 합니다.
groupadd [group]
/etc/group
예를 들어, groupadd test_group을 한 후,
cat /etc/group을 하면 아래와 같이 test_group이 잘 추가 되었음을 볼 수 있습니다.
usermod -a -G [group] [사용자아이디]
chown [소유자]:[그룹] [폴더이름]
해당 폴더의 소유자와 그룹을 변경한다.
chmod xxx [폴더이름]
이 때 xxx에는 0~7사이의 숫자가 들어갑니다. 이 숫자에 따라서 소유자권한, 그룹권한, 기타 사용자권한이 나뉜다. 이것에 관해서는 다른 포스트를 참조하길 바랍니다.
이 정도만 할 줄 알아도 우분투에서 그룹 관련된 권한 관리는 웬만한 건 다 할 수 있을 거라 생각합니다.
리눅스 파일 이름으로 하위 디렉토리의 파일 삭제 (0) | 2018.02.15 |
---|---|
리눅스 현재 폴더 내 모든 zip 파일 압축 풀기 (0) | 2018.02.10 |
XPress Engine 설치 방법 (0) | 2017.01.24 |
리눅스 하위 디렉토리의 특정 파일 삭제
$ find . -name '*.tmp' -exec rm {} \;
설명
find . -name '*.tmp' 는 현재 디렉토리 (.) 아래의 확장자가 tmp인 모든 파일을 찾아 출력한다. 디렉토리를 지정하고 싶다면 . 대신에 디렉토리 path를 찾아서 쓰면된다.
-exec rm {} \; 에서 {}는 find로 찾은 파일들을 나타낸다. -exec rm을 통해 찾은 파일들을 삭제한다
우분투 그룹 관련 명령어 정리 (0) | 2018.08.03 |
---|---|
리눅스 현재 폴더 내 모든 zip 파일 압축 풀기 (0) | 2018.02.10 |
XPress Engine 설치 방법 (0) | 2017.01.24 |
현재 폴더 내 모든 zip 파일의 압축을 푼다.
for file in `ls *.zip`; do unzip "${file}" -d "${file:0:-4}"; done
현재 폴더에 zip 파일의 이름과 같은 폴더가 생성이 되고, 각각의 폴더 아래에 zip 파일의 내용물들이 생성된다.
예를 들어, 1.zip, 2.zip 파일이 있으면, 1, 2폴더가 생성되고 그 아래에 각각 내용물이 생김
현재 폴더 내 모든 gz파일의 압축을 푼다.
for file in `ls *.gz`; do gunzip "${file}"
-d "${file:0:-4}"; done
for file in `ls *.zip`; do unzip "${file}"; done
우분투 그룹 관련 명령어 정리 (0) | 2018.08.03 |
---|---|
리눅스 파일 이름으로 하위 디렉토리의 파일 삭제 (0) | 2018.02.15 |
XPress Engine 설치 방법 (0) | 2017.01.24 |
나눔바름고딕 CSS 적용
폰트 정의
@font-face { font-family: 'NanumBarunGothic';
src: url('/fonts/NanumBarunGothic.eot');
src: url('/fonts/NanumBarunGothic.eot') format('embedded-opentype'),
url('/fonts/NanumBarunGothic.woff') format('woff');}
폰트사용시
body {font-family: 'NanumBarunGothic', 'serif';}
SRC 부분은 웹프로그램 소스상에서 폰트 파일들이 어디에 위치하는지를 적어줍니다.
나눔고딕 CSS 적용
폰트 정의
@font-face { font-family: 'NanumGothic';
src: url('/fonts/NanumGothic.eot');
src: url('/fonts/NanumGothic.eot') format('embedded-opentype'),
url('/fonts/NanumGothic.woff') format('woff');}
폰트사용시
body {font-family: 'NanumGothic', 'serif';}
Python 을 통한 간단한 Web API 구축: Django REST Framework (0) | 2019.07.17 |
---|---|
Aspect Oriented Programming(관점지향프로그래밍) 소개 (4) | 2017.02.02 |
구글의 PaaS(Platform as a Service) 구글 앱 엔진(Google App Engine)에는 크게 두 가지 환경, standard environment와 flexible environment가 있습니다.
저 같은 경우 아무 생각도 없이 node.js google app engine 어플리케이션을 개발한 후 서버에 deploy 했는데, 지출 한도를 0으로 설정했음에도 불구하고 과금이 계속되어 결국에 25달러를 날리게 된 경험이 있습니다..
찾아보니 node.js의 경우 flexible 환경에서만 지원되고, 지출 한도는 standard environment에 적용되는 것이었습니다. 또한 flexible 환경에서는 cpu, memoery 등을 사용한 시간에 따라 과금되는 것이더라구요. 그래서 deploy가 된 시간만큼 요금이 나왔던 것이었습니다.
아래는 두 가지 환경을 비교한 표입니다.
<https://cloud.google.com/appengine/docs/the-appengine-environments>
참고
https://cloud.google.com/appengine/docs/the-appengine-environments
가비아 도메인과 구글앱엔진 연동 (2) | 2018.08.28 |
---|---|
Google App Engine - Error: Could not load the default credentials. (1) | 2017.02.05 |
구글 클라우드 SDK로 Python 웹 애플리케이션 구축하기 (2) | 2016.10.29 |
Google App Engine + node js 환경의 sample 어플리케이션인 BookShelf 어플리케이션을 로컬에서 개발하려던 중,
npm start를 통해 어플리케이션을 실행한 후 홈에 접속하려고 할 때 아래와 같은 문제가 있었다.
Error: Could not load the default credentials. Browse to https://developers.google.com/accounts/docs/application-default-credentials for more information.
문제는 위의 에러 코드에서도 나오듯 아래 URL에서 확인할 수 있는데
https://developers.google.com/accounts/docs/application-default-credentials
Google App Engine 어플리케이션을 로컬에서 작동시키려면 Google App Engine API의 Credential이 필요하다.
아래와 같은 방식으로 해결하였다. Google Cloud SDK가 설치된 상태에서 콘솔창에
gcloud auth application-default login
를 입력한다. 이후 웹 브라우저가 자동으로 실행되고 자신의 구글 계정으로 로그인하여 클릭을 하면 credential 파일이 로컬에 다운로드되고 알아서 그 파일을 어플리케이션 안에서 default credential로 잡게 되는 것 같다.
이후 다시 npm start를 통해 어플리케이션을 실행하면 Bookshelf 어플리케이션이 잘 구동되는 것을 확인할 수 있다.
가비아 도메인과 구글앱엔진 연동 (2) | 2018.08.28 |
---|---|
Google App Engine - standard vs flexible (2) | 2017.02.13 |
구글 클라우드 SDK로 Python 웹 애플리케이션 구축하기 (2) | 2016.10.29 |
AOP란 무엇일까
저는 스프링을 공부하다 AOP를 처음 알게되었습니다.
AOP를 접하고 AOP가 도대체 뭔지, 어떻게 구현하는건지 궁금해서 몇 일 공부해봤습니다.
이 포스팅이 AOP가 무엇인지 궁금하신분들께 도움이 되었으면 좋겠습니다.
AOP의 정의
AOP는 한국에서 관점지향 프로그래밍, 또는 상황중심 프로그래밍으로 번역되는 하나의 프로그래밍 방법론입니다.
AOP는 절차지향이나 객체지향 프로그래밍처럼 그 자체로 하나의 프로그램을 형성할 수 있는 건 아닙니다.
하지만 AOP는 객체지향 코드 위에서 이루어지며 객체지향을 보조하는 역할을 한다고 보시면 됩니다.
AOP의 등장배경
AOP의 등장배경을 알기 위해 간단하게 프로그래밍 방법론의 발전과정을 말해보겠습니다.
처음 프로그래밍 언어가 생겨나고 프로그램이 생기기 시작했을 때 그 규모는 매우 작았습니다.
예를 들어 미사일의 사거리를 계산한다거나 식을 계산한다던가 하는 것이었습니다.
이 때의 프로그래밍 방식을 절차지향 프로그래밍이라고 합니다.
절차지향은 매우 직관적입니다. 소스크드를 위에서 아래로 훑으면서 실행됩니다.
규모가 매우 작았기 때문에 프로그램을 효율적으로 작성하는 방식의 필요성이 제기되지 않았습니다.
하지만 프로그램을 일반 기업에서 사용하게 되면서 점점 프로그램의 규모가 커지기 시작합니다.
그러자 소프트웨어 개발 시장은 위기에 처합니다. 절차지향 방식은 큰 프로그램을 만드는데 매우 비효율적이었습니다.
이제 소프트웨어 시장에서는 거대한 프로그램을 개발하고 유지보수하기 위한 새로운 방법론이 필요했습니다.
이 때 등장한 것이 객체지향프로그래밍(OOP : Object Oriendted Programmin)입니다.
OOP는 객체(Object)라는 혁신적인 개념을 활용함으로써 큰 프로그램을 모듈단위로 축소시켜
작성할 수 있게 함으로써 이 위기를 극복합니다.
하지만 이러한 객체지향방식에도 허점이 있었습니다.
객체지향 방식의 장점중의 하나는 프로그램을 모듈화 시켜 이를 재활용함으로써
코드의 중복을 줄이고 코드의 재사용성을 높이는 것입니다.
하지만 프로그램의 크기가 엄청나게 커지면서 이러한 모듈 안에서마저 중복되는 코드가 생기게 되는 것이었습니다.
이를 횡단 관심사(Crosscutting-Concerns)라고 합니다. 그 중 자주 언급되는 것이 바로 트랜잭션, 로깅, 성능 분석 등입니다.
이러한 횡단 관심사들은 여러 모듈들을 말 그래도 횡단하면서 존재하게됩니다.
횡단 관심사는 AOP를 이해함에 있어서 매우 중요한 개념입니다. 포스트 맨 위 사진이 바로 AOP의 개념도입니다.
AOP의 목적은 바로 이러한 횡단관심사를 모듈화 하는 방법을 제시하는 것입니다.
이를 통해 코드의 중복을 제거하고 이해하기 쉽게하게 프로그램의 작성을 쉽게하고 유지보수를 편리하게 할 수 있습니다.
AOP의 구현
AOP의 구현하는 방법에는 여러가지가 있습니다.
스프링 AOP에서는 애노태이션과 xml을 활용하여 보통 소스에서는 보이지 않게합니다.
하지만 지금은 AOP가 무엇인지 이해하는게 목적이므로 친숙한 자바 코드로 설명해보겠습니다.
아래의 Java 코드를 봅시다.
public class BoardService { public void addArticle(BoardBean board){ try { boardDao.add(board); // 비지니스 로직 memberDao.levelUp(board.userId); this.transactionManager.commit(); // 커밋 } catch(Exception e){ this.transactionManager.rollback(); // 롤백 } } }
위의 BoardService 클래스의 addArticle 메소드를 보면 boardDao.add(board), memberDao.levelUp(board.userId)라는 비지니스 로직과 트랜잭션에 관련된 코드가 섞여 있음을 볼 수 있습니다. 사실 대부분의 프로그래머들은 소스를 위처럼 짤 것입니다. 위 처럼 짜도 그렇게 잘못된 코드는 아닌 것 같습니다. 하지만 트랜잭션을 적용하는 메소드들이 많아지면 많아질 수록 트랜잭션 관리 코드가 늘어나게 되고 이를 일괄적으로 관리하게 어려워지는 것이 사실입니다.
위 코드를 이처럼 바꿔봅시다.
class BoardServiceTx implements BoardService { BoardService boardService = new BoardServiceImpl(); public void addArticle(BoardBean board){ try { boardService.addArticle(board); // 위임 this.transactionManager.commit(); } catch(Exception e){ this.transactionManager.rollback(); } } } class BoardServiceImpl implements BoardService { public void addArticle(BoardBean board){ try { boardDao.addArticle(board); memberDao.levelUp(board.userId); } } }
BoardServiceTx 는 트랜잭션을 구현하는 코드이고 BoardServiceImpl은 비지니스 로직을 구현하는 코드입니다.
BoardServiceTx 는 비지니스로직을 BoardServiceImpl 에게 위임하고 있습니다.
하나의 클래스를 둘로 분리하면서 트랜잭션과 비지니스 로직을 분리하는데 성공했습니다.
하지만 메소드들이 더 많아지면 try catch 문은 계속해서 쓰이게 됩니다.
트랜잭션과 비지니스 로직 두 개의 관심사를 분리하였지만 중복을 제거하지는 못한 것입니다.
이제 우리의 목적은 트랜잭션 코드를 딱 한 번만 쓰는 것입니다.
저 try catch 문을 한 번만 쓰게 되면 작성할 코드의 양을 현저하게 줄일 수 있고 관리하기도 편해질 것입니다.
이것을 위해서는 자바의 리플렉션에 대해서 먼저 알아야합니다.
public static void main(String[] args){ String name = "AOP"; Method lengthMethod = String.class.getMethod("length"); int length = lengthMethod.invoke(name); }
위 코드에서 length에는 3이 저장됩니다. String 클래스의 length 메소드를 Method라는 객체에 저장시키고
이를 invoke라는 메소드를 통해서 실행시킬 수 있습니다!
이러한 invoke라는 개념을 활용한게 자바의 InvocationHandler 인터페이스와 Proxy객체입니다.
아래는 InvocationHandler를 구현하는 TransactionHandler 클래스입니다.
public class TransactionHandler implements InvocationHandler { private Object target; private PlatformTransactionManager transactionManager; private String pattern; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ invokeInTransaction(method, args); } private Object invokeInTransaction(Method method, Object[] args) throws Throwable { TransactionStatus status = this.transactionManager.getTransaction (new DefaultTransactionDefinition()); try { Object ret = method.invoke(target, args); this.transactionManager.commit(); return ret; } catch { return method.invoke(target, args); } } }
위의 TransactionHandler는 InvocationHandler를 구현하고 있습니다.
이렇게 한 후에 아래처럼 Proxy.newProxyInstance의 3번째 인자로
TransactionHandler의 인스턴스인 txHandler를 넣어주면 boardService의 모든 메소드를 실행할 때
TransactionHandler의 invoke 메소드를 실행하게 됩니다.
신기하지 않으신가요? 자바 API로 가능한 것들입니다. 지금은 저 복잡한 문법에는 신경 쓰지 않으셔도 됩니다.
AOP를 이해하는게 이 포스팅의 목적이니까요..
class BoardController { TransactionHandler txHandler new TransactionHandler(); txHandler.setTarget(new BoardServiceImpl()); txHandler.setTransactionManager(transactionManager); BoardService boardService = (BoardService)Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {BoardService.class}, txHandler); public void addArticle(BoardBean board){ boardDao.addArticle(article); memberDao.levelUp(board.userId) } }
위의 BoardController 코드는 실제로 AOP를 사용하는 코드입니다. (메인함수라고 보시면됩니다.)
사용자가 글을 쓸 때 addArticle이라는 메소드가 호출될 것입니다.
addArticle 메소드에는 트랜잭션이 적용되었지만 트랜잭션에 관한 코드는 보이지 않습니다.
이렇게 boardService에 Handler를 적용시킴으로써 비지니스 로직과 트랜잭션이라는 두 개의 관심사가 분리되었고
트랜잭션에 관련된 코드를 한 곳으로 모아 작성할 코드의 양을 줄이고 관리하기도 편하게 되었습니다.
이로써 AOP의 목적에 부합하는 코드를 실제로 구현해 보았습니다.
물론 이게 AOP의 끝은 아닙니다. 맛보기라고 보시면 됩니다. 실무에서는 여러가지 애노테이션과 xml을 활용하기 때문에 AOP를 제대로 활용하고 싶으시다면 AspectJ 또는 Spring AOP를 학습하시면 좋습니다.
Python 을 통한 간단한 Web API 구축: Django REST Framework (0) | 2019.07.17 |
---|---|
나눔바른고딕, 나눔고딕 CSS 적용하기 (0) | 2017.11.13 |
XECore 설치
https://www.xpressengine.com/download
Autoset 설치
http://autoset.net/xe/download_autoset_9_0_0
참고
결과
http://127.0.0.1/xe 접속시 아래와 같은 샘플 어플리케이션을 볼 수 있음
우분투 그룹 관련 명령어 정리 (0) | 2018.08.03 |
---|---|
리눅스 파일 이름으로 하위 디렉토리의 파일 삭제 (0) | 2018.02.15 |
리눅스 현재 폴더 내 모든 zip 파일 압축 풀기 (0) | 2018.02.10 |