회사에서 처음 알게된 Mroonga인데요. 원래 InnoDB를 쓰면 Fulltext 검색을 사용할 수 없어요. MyISAM에서 밖에 안되죠. Mroonga는 InnoDB의 특징인 트랜잭션, 외래키락 등을 지원하고 Fulltext 검색까지 원하는 엔진이에요. 

마리아디비가 10.0.15로 업데이트되면서 Mroonga가 기본적으로 포함이 되어 있어요.


1. 마리아디비 최신버전 설치

https://downloads.mariadb.org/mariadb/repositories/#mirror=kaist

리눅스계열은 소스를 받아서 컴파일하는 것보다 위처럼 레포지토리를 추가해서 하라는대로 해서 설치하는 게 빠릅니다. 위처럼 설치하면 잘 설치가 될꺼에요. 저는 우분투라 아래와 같이 설치했습니다.

sudo apt-get install software-properties-common
sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db
sudo add-apt-repository 'deb http://ftp.kaist.ac.kr/mariadb/repo/10.0/ubuntu trusty main'

키를 추가하고 레포지토리를 추가하면 최신버전의 mariadb를 설치할 수 있습니다.

sudo apt-get update
sudo apt-get install mariadb-server


2. Mroonga 플러그인 설치하기

원래 Mroonga도 소스받아서 컴파일하고 설치해야하는데, 최신버전인 10.0.15가 설치가 되어있다면 그냥 바로 아래 명령어로 플러그인이 설치가 됩니다.

deploy@jared-dev:/usr/bin$ mysql -uroot -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 38
Server version: 10.0.15-MariaDB-1~trusty mariadb.org binary distribution

Copyright (c) 2000, 2014, Oracle, SkySQL Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> INSTALL SONAME 'ha_mroonga';
Query OK, 0 rows affected (0.04 sec)

MariaDB [(none)]> show engines;
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                                    | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables                  | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                                         | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                                         | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears)             | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                                      | NO           | NO   | NO         |
| MRG_MyISAM         | YES     | Collection of identical MyISAM tables                                      | NO           | NO   | NO         |
| Mroonga            | YES     | CJK-ready fulltext search, column store                                    | NO           | NO   | NO         |
| FEDERATED          | YES     | FederatedX pluggable storage engine                                        | YES          | NO   | YES        |
| InnoDB             | DEFAULT | Percona-XtraDB, Supports transactions, row-level locking, and foreign keys | YES          | YES  | YES        |
| Aria               | YES     | Crash-safe tables with MyISAM heritage                                     | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                                     | NO           | NO   | NO         |
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
11 rows in set (0.01 sec)

MariaDB [(none)]> 


3. Mroonga로 테이블 생성, InnoDB로 테이블 생성 후 비교.

Mroonga로 만드려면 아래와 같이 Engine은 Mroonga로 쓰고, fulltext key를 추가하고, fulltext key에 코멘트로 검색 옵션을 지정하면 돼요. 그리고 Engine에 코멘트를 달 수가 있는데, 이것은 InnoDB에 Wrapper Mode로 사용하겠다는 것이에요. 기본적으로 Mroonga를 생성하면 Storage Mode가 되는데, 이걸로 만들면 Transaction을 사용할 수 없어요.

CREATE TABLE `random_hangul` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `hangul` text NOT NULL, PRIMARY KEY (`id`), FULLTEXT KEY `hangul` (`hangul`) COMMENT 'parser "TokenBigramIgnoreBlankSplitSymbolAlphaDigit"' ) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"'

아래는 그냥 InnoDB

CREATE TABLE `random_hangul2` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `hangul` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

데이터를 아래와 같이 허접한 php스크립트로 넣고-_- 넣다보니 120만개 넣었는데, 지금부터 막 느려지더라구요ㅠ 구린 컴이라ㅠ

<?php

$servername = "localhost";
$username = "root";
$password = "1234";
$db = "mudchobo";

$words = ['가', '나', '다', '라', '마', '바', '사', '아', '자', '차', '카', '타', '파', '하'];

// Create connection
$conn = mysqli_connect($servername, $username, $password, $db);

// Check connection
if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}
echo "Connected successfully";
$conn->query("set names utf8");
for ($i = 0; $i < 1000000; $i++) {
    $randomWords = '';
    for ($j = 0; $j < 100; $j++) {
        $randomWords .= $words[mt_rand(0, count($words) - 1)];
    }
    echo 'randomWords = ' . $randomWords . PHP_EOL;

    $conn->query("insert into random_hangul(hangul) values ('$randomWords')");
}

$conn->close();


그리고 검색하면....뭐 거의 비슷한데....이상하네... 암튼 그래도 조금 더 빠르네요. 아 잘모르겠네요... 동시접속하면 더 빠르려나요 ㄷㄷ

MariaDB [mudchobo]> select count(*) from random_hangul;
+----------+
| count(*) |
+----------+
|  1298192 |
+----------+
1 row in set (0.00 sec)

MariaDB [mudchobo]> select * from random_hangul where match(hangul) against('가나다' in boolean mode);
...
45681 rows in set (1.69 sec)

MariaDB [mudchobo]> select count(*) from random_hangul2;
+----------+
| count(*) |
+----------+
|  1297523 |
+----------+
1 row in set (0.00 sec)

MariaDB [mudchobo]> select * from random_hangul2 where hangul like '%가나다%';
...
45904 rows in set (3.53 sec)

암튼 Fulltext에 옵션이 있는데, 공백과 검색, or and검색 등 다양한 옵션을 제공하니 참고요.
http://mroonga.org/docs/tutorial/wrapper.html#how-to-use-wrapper-mode

확실히 간단하게 사용할 검색이라면 쓰기에 좋을 것 같습니다.

 
Posted by 머드초보
,
 

회사에 CI서버 셋팅할라고 삽질하는데 Ruby가 컴파일 하는데 겁나 에러나서 기록용-_-

yum install ruby로 설치되는 루비 버전이 2.1이여서 2.2.0을 설치하기 위해서 rbenv를 설치하려고 했는데, rbenv는 사실 그냥 스크립트라서 git으로 받으면 되는것인데, rbenv가 문제가 아니라 2.2.0이 컴파일이 잘 안되었어요.그래서 열심히 로그를 살펴본 결과 의존성패키지를 덜 설치해서 그렇더라구요.

아래사이트는 6.5에서 하는 방법인데, 여기에서 몇 개 더 설치를 해줘야 해요.

http://mmclub.github.io/blog/2014/03/30/install-ruby-on-rails-on-centos/


1. 필요 패키지 설치

sudo yum update
sudo yum install git
sudo yum groupinstall -y 'development tools'
sudo yum install -y gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel  sqlite-devel
sudo yum install glibc-devel libffi-devel

맨 아래줄에 있는 거 설치안하면 컴파일할 때 에러나더라구요. 


2. rbenv 설치

git clone git://github.com/sstephenson/rbenv.git ~/.rbenv

다른 계정에서도 실행할 수 있게 하려면 보통 /usr/local/rbenv 위치에 설치하기도 합니다. 


3. ruby-build 설치

git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

rbenv에 설치한 경로 안에 plugins/ruby-build 디렉토리에 설치합니다.


4. 환경변수 설정

글로벌 하게 쓰려면 /etc/profile.d/rbenv.sh로 파일을 생성해서 전체 유저에 영향받게 하고, 해당 유저만 사용하려면 그냥 .bashrc에 넣어도 됩니다.

일반적으로 RBENV_ROOT가 ~/.rbenv로 되어 있습니다. 이걸 바꾸고 싶으면 RBENV_ROOT환경변수를 정의하면 됩니다. 

rbenv를 /usr/local/rbenv에 설치한 경우 /etc/profile.d/rbenv.sh 추가

export RBENV_ROOT="/usr/local/rbenv"
export PATH="$RBENV_ROOT/bin:$PATH"
eval "$(rbenv init -)"

그냥 .rbenv에 설치한 경우에는 rbenv path만 잡아주면 됩니다. (~/.bashrc)

export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"


5. 루비를 설치

rbenv install 2.2.0
rbenv rehash
rbenv global 2.2.0
ruby -v
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]


6. 레일즈도 해볼까...

gem install rails


오 잘된다!

 
Posted by 머드초보
,
 

스마트폰을 아이폰6플러스로 바꿨습니다. 그 동안 할부원금(?) 아이폰보다 싼 안드로이드폰을 계속 써왔는데요.

처음에 멋도 모르고 난 구글이 좋으니까 한국 최초 출시된 안드로이드폰인 모토로이 쓰다가 SKT안드로이드 공모전에서 앱을 내고 받은 갤럭시S! 그러다가 갤럭시S3 대란이 뽐뿌발로 펼쳐지면서 탑승! 그 뒤에 G2가 미친듯한 가격하락으로 급뽐뿌가 와서 구매! 

골드 + 에어자켓(클리어메이트)입니다! 겁나 이뻐!


이러다보니 가격 방어 잘 되는 아이폰 계열을 쓸 기회가 없었네요. 그러다가 회사에서 아이폰6플러스를 줘서 갈아타게 되었습니다(사...사....아니 감사합니다 회사님ㅠ). 

확실히 안드로이드를 쓰다가 아이폰을 쓰니 장점도 있고, 단점도 있네요. 외관이야 뭐 많이 봤을테니 제가 주로 사용하는 기능 위주로 리뷰를...(뭐 주로 하는 게 게임이지만ㅠ)

가장 기본적인 음악과 동영상 기능부터...

1. 음악 넣기

안드로이드폰은 그냥 컴퓨터랑 연결해서 mp3파일을 복사하고 음악플레이어를 실행하면 자동으로 파일을 스캔해서 리스트에 떠서 바로 재생할 수 있는 구조입니다. 그래서 보통 음악 폴더를 통째로 올리고, 음악폴더별로 재생이 가능하고, 재생목록도 만들 수 있어서 듣고 싶은 음악 리스트를 넣을 수 있습니다.

아이폰에서는 mp3를 폴더로 복사하는 기능이 없고(뭐 다른 방법이 있겠지만 보편적으로 itunes로 해야하는 것 같더라구요), 아이튠즈에서 음악을 드래그로 추가해서 재생목록을 만든 뒤 동기화를 시키면 바로 아이폰에서 동일한 재생목록으로 그대로 들을 수 있습니다. 맥과 아이폰끼리 서로 완벽히 동기화가 된다는 장점이 있습니다. 맥에서 재생목록만 잘 관리하면 매우 편합니다.

아..걸스데이 노래 겁나 좋네요. 몇 년을 들어도 안질려요.

그리고 아이폰 전용 이어폰이 꽤 맘에 드네요. 귀에 쏙 맞고 음질도 꽤 괜찮아요(이어팟이라는 이름이 있네요). 그리고 다른 것보다 맘에 드는 것은 맥북에 연결하면 맥북 볼륨 조절도 된다는 것입니다!! 회사 맥북에 모니터 연결해서 쓰고 있는데, 볼륨 조절 하려면 맥북에 있는 fn+F10~F12로 조절해야 해서(윈도우 세대로 fn키를 켜놨...) 귀찮았는데, 바로 누르니 볼륨 조절이 됩니다.


2. 동영상 넣기

안드로이드는 음악처럼 동영상 파일 그대로 안드로이드폰에 복사해버리면 플레이어가 알아서 인식해서 재생할 수 있습니다. 음악이랑 똑같죠. 그리고, 보편적인 코덱은 대부분 지원해서 복사만하면 재생못하는 영상은 거의 없습니다. 그래서 꽤나 편했죠.

근데, 아이폰은 비디오 재생을 하려면 기본 내장된 비디오앱에서 iTunes에서 구매를 하거나 인코딩해서 동영상을 동기화하는 방법이 있습니다. 

다른 방법으로는 써드파티 동영상앱을 설치하고, 아이튠즈를 통해 파일공유라는 기능을 이용해서 무인코딩 동영상을 넣어서 재생할 수 있습니다. 대부분 써드파티 동영상앱으로 하는 방법을 선호하죠. 아래 사이트에 잘 나와 있네요.

http://azdesigntm.com/700


우사미짱. 잘 나오네요. kmp플레이어에요.

동영상도 위와 같은 방법으로 넣으면 쉽게 넣을 순 있어요. 근데, 코덱이 재생할 수 있는 앱이 있고, 재생할 수 없는 앱들이 있어요. 동영상 앱 선택할 때 신중히 선택하셔서 구매하세요.


3. 가로로 전환?

이건 쓰면서 느낀건데 제 폰이 맛이 간건지 원래 그런건지는 정확하게는 잘 모르겠어요. 안드로이드 같은 경우는 폰을 애초부터 가로 거꾸로 잡고 있고, 거꾸로 잡고 있으면 앱이 실행될 때 자동으로 거꾸로 된 위치가 바뀌는데, 아이폰은 안그렇더라구요. 애초에 거꾸로 잡고 가로앱을 실행하면, 계속 거꾸로 되는데, 다시 폰을 돌렸다가 다시 거꾸로 돌리면 그때 화면이 바뀌더라구요. 뭐 별거 아니지만 그냥 그렇다구요... 써놓고 보니 뭔말이지....무...무시하세요...


4. 스팸전화 필터 앱

안드로이드폰은 전화올 때 이벤트 같은 것을 써드파티앱이 받을 수 있습니다. 그래서 후후, 후스콜 처럼 전화가 오면 그 전화번호를 자신의 스팸데이터에서 검색을 해서 이게 스팸전화인지 아닌지를 알 수 있는 앱들이 있죠.

아이폰에서는 이런 필터앱 구현을 할 수 없습니다. 그래서 "뭐야 이번호"라는 앱이 있는데, 아이폰에서는 연락처에는 접근할 수 있어서 그 연락처에 모든 스팸번호를 등록해서 그 스팸번호로 전화가 오면 뭐야이번호로 저장된 번호로 전화가 옵니다. 

최신정보를 앱을 실행해서 받아줘야하는 단점이 있습니다ㅠ


스팸전화 두 개 걸렀네요.

위와 같이 전화가 오면 뭐야이번호로 오기 때문에 안받으면 됩니다. 연락처에 다양한 전화번호가 저장될 수 있어서 가능한 것 같습니다. 만든 사람 머리 좋네요. 스팸전화를 어제 두 건이나 받았는데, 걸렀네요.

또 다른 방식의 스팸필터앱 중에 후스콜(안드로이드 처럼 실시간 앱이 아님)이라는 게 있는데, 이것은 모르는 번호에 전화가 오면 받지 않고 끊은 다음에 전화번호를 복사하면 그 번호로 인터넷을 통해 검색을 해서 위젯에 표시해주는 원리의 앱입니다.
전화번호에서 최근통화해서 i버튼을 누르면 전화번호가 나오는데, 그거 오래 누르고 있으면 복사하기가 되는데 복사하자마자 후스콜이 바로 검사를 해줍니다. 위에서 위젯을 보여주면 이게 스팸인지 아닌지 나와요......귀찮아 하는 사람들은 전화받고 스팸이면 끊는 게 더 낫겠어요.

텔레마케팅이라고 나오네요.


5. 움직이는 아이콘!

오 신기했던 게 바탕화면에 있는 아이콘에 오늘 날짜가 표기되고, 시계는 초침이 움직이면서 현재 시계가 아날로그로 표기되고 있어요! 근데, 이거 개발자가 앱으로 구현할 수는 없는거죠? 이런 종류의 앱을 본 적이 없어서... 암튼 신기한 아이콘이네요. 아 근데 생각해보니 안드로이드는 위젯이 있으니...별로 신기할 것도 없는데, 뭔가 더욱 깔끔해보여서 신기하게 느껴지는 것 같네요.


6. 웹브라우저 사파리(Safari)

저는 크롬이 좋아서 크롬을 깔았는데, 깔자마자 네이버들어가고 바로 지웠습니다. 속도가 안드로이드폰이나 기본 아이폰 사파리에 비해서 너무 느렸어요. 그래서 그냥 사파리를 쓰기로 했습니다. 

브라우저는 빠릿빠릿해서 좋네요. 더욱 좋은 점은 컨트롤인데, 처음에 안드로이드는 뒤로가기 할 때 그냥 뒤로가기 버튼 누르면 알아서 뒤로 가는데, 아이폰은 그 버튼이 없어서 뒤로가기 하려면 버튼을 눌러야 하는데, 버튼을 누르려면 스크롤을 다시 올려야 메뉴가 나오는데, 그때 좌측하단에 있는 버튼을 눌러야 뒤로 갈 수 있습니다. ㅣㄴ아러ㅣㅏㅁㄴㄹ얼ㅇ 아오 뒤로가기 겁나 힘드네 하고 있는데, 다른 사람들 쓰는 거 보니 왼쪽끝에서 오른쪽으로 드래그를 하니 뒤로 가집니다!

전체 열려 있는 탭이 간지나게 나오네요. 역시 UI는 애플이죠. 왼쪽으로 밀면 바로 닫을 수 있어요 짱이에요.


드래그로 뒤로가기를 쉽게 할 수 있어요!


7. 건강

오 이런 기능은 정말 처음이네요. 안드로이드에는 없어요ㅠ 자신이 얼마나 걸었는지 계단 오르내리기를 얼마나 했는지 그래프로 이쁘게 보여줍니다. 아...볼 때마다 운동해야지 하는데...회사가 코앞이라... 하루에 2km미만으로 걷고 있네요ㅠ

이제 계단으로 출퇴근해야겠다...3계단이라니...


8. 핫스팟 기능

핫스팟이 좀 맘에 안드는 게 이상하게 연결이 잘 안됩니다ㅠ 제가 안드로이드 게임밖에 없는 게임을 위해 폰을 안드로이드폰과 같이 들고 다니는데, 핫스팟기능을 켜면 한 번에 연결이 안될 때가 많습니다(설마 이것도 내 폰만 문제인건가ㅠ). 몇 번 다시 껐다켜면 연결이 되더라구요. 

좋은 점이 폰이 연결이 되면 몇 개 연결이 되었는지 상단에 나와요. 그래서 어떤 놈이 내꺼 몰래 쓰고 있는지 나오죠. 그리고, 안드로이드는 테더링이라고 표시하는데, 타임아웃 시간을 지정할 수 있습니다. 무제한까지 가능한데, 아이폰은 타임아웃 시간을 지정하는 것이 없고, 그냥 한 5분동안 인터넷을 하지 않으면 끊어지는 느낌입니다ㅠ 끊어지면 또 바로 연결이 안되고, 핫스팟 기능을 다시 껐다 켜야 하더라구요.  

우오오! 저기 선택하면 설정화면으로 가요.


9. NFC기능이 없...

이거 좀 아쉽습니다. 원래 NFC가 아이폰에 들어는 있는데, 애플페이 만을 위한 기능이라고 하네요. 예전에 안드로이드폰에는 NFC가 있어서 체크카드가 없을 때 NFC를 이용해서 ATM에서 돈을 뺄 수 있는데, 그걸 할 수 없어요ㅠ 교통카드 기능도 되는데ㅠ 애플이 하루 빨리 풀어줘야 한다고 생각합니다.


10. 지문인식 기능

지문인식 기능 겁나 신세계입니다. 화면 잠금 풀 때 자연스럽게 가운데 버튼을 누르면 잠금이 풀립니다. 이렇게 지문인식이 신기하게 잘 되는 거 처음보네요. 예전에 제 노트북이 소니지문인식노트북이였는데, 살 때 처음 테스트해보고 봉인했었는데, 이건 인식율이 너무 좋으니 이걸 활용한 앱들이 나오면 좋을 것 같습니다(아..근데 이것도 사용자가 구현할 수 있나 모르겠네요). 

그리고, 앱스토어에서 앱을 구매 및 다운로드할 때에도 비밀번호 겁나 쳐야하는데, 지문인식만으로 앱을 구매 및 다운로드할 수 있어요. 근데 안드로이드에서는 사실 무료앱 다운로드할 때에는 비밀번호 안물어보죠. 유료앱살 때 결제할 때에만 비밀번호를 물어보죠. 앱스토어도 무료앱살 때에는 비번 안치게 좀......


11. 게임

게임은 안드로이드폰보다 훨씬 부드럽게 잘 돌아가네요. 요즘 즐겨하는 게임 중 영웅이 있는데요. 확실히 G2에서 하던 것보단 렉도 없이 잘 돌아갑니다. G2에서 할 때에는 몹이 미친듯이 많아지면 렉이 걸려서 죽기 일쑤여서 해상도 조절을 최하로 맞춰놓고 했는데요(그래서 렉걸리고...ㅠㅠ). 아이폰에서는 그런 게 전혀 없어서 좋네요. 고해상도로 해놔도 매우 부드럽게 잘 돌아가요! 게임은 아이폰에서 하는 게 좋을 것 같아요.

단.... 아이폰 게임이.... 출시가.... 잘 .... 안됩...니다.... 카카오게임만 해도 주로 안드로이드만 출시하는 게임이 대부분이에요. 아이폰 유저가 좀 더 늘어나야 게임 개발사들이 아이폰에 더 눈을 돌릴텐데요ㅠ 우리나라 현실은 그렇지 않으니 안타깝네요.


결론

이제 쓴 지 한달이 다되어가는데요. 장점도 있고 단점도 있는 것 같습니다. 일부 안타까운 단점은 뭐 그닥 불편한 점은 아니고, 게임을 주로(?)하기 때문에 매우 만족스럽습니다. 지금 하고 있는 영웅이 매우 잘 돌아가서 기쁘네요ㅠ 그리고 맘에 드는 점은 부드러운 UI전환과 애니메이션입니다. 아주 깔끔해요. 이래서 다들 아이폰아이폰 하나 봅니다. 

인기 있고 유명한 게임(이런 게임들은 대부분 아이폰도 같이 냄!)만 하실 분들은 아이폰 강추합니다. 게임이 잘 돌아갑니다! 최고의 게임기 입니다!

 
Posted by 머드초보
,
 

기존에 syntax highlighter라는 플러그인은 js를 업로드하거나 script로 include해서 <code></code>태그를 찾아서 css를 입히는 형태가 있습니다. 근데 티스토리에서 할라니까 html skin도 편집해야 하고 js, css파일도 업로드해야 합니다. 이거 말고도 https://highlightjs.org/라는 플러그인도 있지만, 이것도 위와 같은 동일한 형태입니다.

결정적으로 글을 쓸 때 html형태는 Syntax Highlight를 하기 힘들더라구요. 그래서 찾다보니 그냥 소스코드를 붙여넣으면 바로 css까지 입힌 형태의 소스코드를 생성합니다. 그래서 티스토리에서 글을 쓸 때 그냥 <p></p>사이에 코드를 그대로 넣으면 매우 잘 나옵니다.

사이트 주소

http://hilite.me/

아래와 같이 그냥 소스코드 대추 붙여놓으면 이쁘게 html로 바꿔서 오른쪽에 표시해줍니다.


아래는 html태그를 붙인 예제입니다.

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<div>
    테스트입니다.
</div>
</body>
</html>


php도 붙여보니 잘되네요.

<?php
$osInfo = strtoupper(substr(PHP_OS, 0, 3));

if (count($argv) <= 4)
{
    echo 'argv enter. area stage isChallenge device deviceip ex) 5 3 1 genymotion (101)' . PHP_EOL;
    exit;
}
$area = $argv[1];
$stage = $argv[2];
$isChallenge = ($argv[3] == '1') ? true : false;
$device = $argv[4];
$data = json_decode(file_get_contents("$device.json"), true);
if ($data == null) {
    echo 'Error!' . PHP_EOL;
    exit;
}


프로그래밍 언어 관련 글 쓰시는 분들 잘 애용하세요. 보니까 소스코드도 공개되어 있더라구요. 혹시나 사이트가 나중에 죽어버려도 소스가 깃헙에 있으니 괜찮을 것 같아요.

 
Posted by 머드초보
,
 

스프링 오랜만에 다시 보내 새롭네요. 우연히 접할 기회가 있어서 스프링 부트를 접하게 되었습니다. 기존에 주로 회사에서는 php, ruby on rails, javascript 등 스크립트 언어를 해오다 보니 꽤나 쉽지 않네요. 뭐 이걸로 프로젝트를 한 번 하면 금방 배우겠죠. 루비 같은 경우도 작년까지 전혀 몰랐는데, 프로젝트를 하다보니 점점 알게 되더라구요. 

1. 셋팅

이걸 하면서 gradle이라는 것을 알게 되었는데, 보니까 maven 같은 것인데, 확실히 maven보단 설정 문법이 더욱 깔끔하고 좋네요. maven에서는 xml 지옥이라 알아보기 힘들었는데, gradle은 그냥 스크립트 형식으로 되어있어서 더 알아보기 쉽게 되어있습니다. 

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.10.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'spring-boot'

sourceCompatibility = 1.5
version = '1.0'

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
    compile("org.springframework.boot:spring-boot-starter-web:1.2.0.RELEASE")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("mysql:mysql-connector-java:5.1.34")
}

맨 위에 buildscript부분은 jar파일 만들기 위해서 필요한 것 같아요. 그 외에 plugin이 spring-boot가 추가되었는데, bootRun 이런 task 등이 추가 되어있어요.

dependencies는 스프링 부트를 쓰려면 spring-boot-starter-web이 있어야 하고, 데이터연동하는 것을 하기 위해서는 spring-boot-starter-data-jpa가 있어야 하고, mysql을 쓰려면 mysql-connector-java를 추가해야 해요.
jpa에는 하이버네이트가 내장되어 있어요.


2. 메인 클래스

스프링 부트는 예전 개발방식이랑 틀리게 톰캣 받고, web.xml을 셋팅하고, 웹개발을 위한 셋팅이 필요없이 기존 java 실행 방식으로 실행하며 이 실행과 동시에 내장된 톰캣이 작동하여 서버를 만들어버립니다. 아래와 같이 메인클래스를 만들어버리면 그냥 웹서버애플리케이션이 뜹니다.

Application.java

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);

    }
}

보면 예전에 자바처음 배울 때 쓰는 public static void main클래스로 실행만 합니다. @SpringBootApplication어노테이션을 붙이면 최초 기본셋팅으로 톰캣을 띄워서 8080포트로 서버를 만들어 줍니다.


3. 모델 생성

그 전에 접속할 db정보를 입력해야 합니다. 
application.properties

spring.datasource.url=jdbc:mysql://127.0.0.1/sosi?autoReconnect=true&useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.database=mysql
spring.jpa.show-sql=true

대략 설정값은 저런데 ddl-auto부분이 create-drop은 서버 재시작 때마다 테이블을 날려버리는 옵션입니다. 처음에 개발할 때에는 매우 편리합니다. 값을 매번 지우지 않고도 서버만 재시작해도 처음부터 다시 개발 해놓은 것을 테스트해볼 수 있으니깐염.

Sosi.java

@Entity public class Sosi { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Column(nullable = false) private String name; @OneToMany @JoinColumn(name="sosi_id", referencedColumnName="id") private List<Schedule> scheduleList; public Sosi() { } public Sosi(String name) { this.name = name; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Schedule> getScheduleList() { return scheduleList; } public void setScheduleList(List<Schedule> scheduleList) { this.scheduleList = scheduleList; } }


Schedule.java

@Entity
public class Schedule {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    @JsonBackReference
    private Sosi sosi;

    @Column
    private String program;

    public Schedule() {
    }

    public Schedule(Sosi sosi, String program) {
        this.sosi = sosi;
        this.program = program;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Sosi getSosi() {
        return sosi;
    }

    public void setSosi(Sosi sosi) {
        this.sosi = sosi;
    }

    public String getProgram() {
        return program;
    }

    public void setProgram(String program) {
        this.program = program;
    }

}

두 개의 모델이 1:N관계 입니다. Sosi가 스케줄을 여러개 가질 수 있는 구조입니다. 여기서 Schedule에는 ManyToOne을 걸었는데, @JsonBackReference도 같이 넣었습니다. 이걸 안 넣으면 서로 계속 참조해서 JSON출력할 때 무한루프에 빠지더라구요-_- 이제 Sosi참조는 json에서 안쓰는 그런 옵션 같습니다.

그리고 예전에는 Dao만들어서 뭔가 하이버네이트세션 가져와서 그걸 통해서 했던 것 같은데, Spring Data JPA에서는 JpaRepository라는 것을 제공하는데, 이것을 통하면 해당 모델에 대해서 CRUD를 제공합니다. 

SosiRepository.java

public interface SosiRepository extends JpaRepository<Sosi, Long>{
}

ScheduleRepository.java

public interface ScheduleRepository extends JpaRepository<Schedule, Long> {
}


4. 컨트롤러 생성

간단하게 소녀시대 정보를 가져오는 컨트롤러와 스케줄 추가 및 가져오는 컨트롤러를 생성합니다.

SosiController.java

@RestController
@RequestMapping("/sosi")
public class SosiController {

    @Autowired
    private SosiRepository sosiRepository;

    @RequestMapping("{sosiId}")
    public Sosi getSosi(@PathVariable Long sosiId) {
        Sosi sosi = sosiRepository.findOne(sosiId);
        return sosi;
    }
}

ScheduleController.java

@RestController
@RequestMapping("/schedule")
public class ScheduleController {

    @Autowired
    private ScheduleRepository scheduleRepository;

    @Autowired
    private SosiRepository sosiRepository;

    @RequestMapping("{scheduleId}")
    public Schedule getSchedule(@PathVariable Long scheduleId) {
        Schedule schedule = scheduleRepository.findOne(scheduleId);
        Sosi sosi = schedule.getSosi();
        return schedule;
    }

    @RequestMapping("add/{sosiId}")
    public Schedule addSchedule(@PathVariable Long sosiId, @RequestParam(value="program") String program) {
        Sosi sosi = sosiRepository.findOne(sosiId);
        Schedule schedule = scheduleRepository.save(new Schedule(sosi, program));

        return schedule;
    }
}

소스 내용을 보면 Repository클래스를 통해 Autowired하면 기본 인터페이스를 해당 모델기반 구현체를 만듭니다. 그 구현체에서 save, findOne 등 함수를 통해 데이터 삽입 및 가져올 수 있습니다.


5. 기본 값 삽입

기본적으로 소녀시대 멤버를 삽입하고 시작할 수 있습니다. 기존 application.properties에 하이버네이트 설정을 재시작하면 꺼지게 해놨기 때문에 기본적으로 앱을 시작할 때 값을 삽입하고 하면 편하게 테스트할 수 있습니다.

Application.java

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        SosiRepository sosiRepository = context.getBean(SosiRepository.class);

        sosiRepository.save(new Sosi("태연"));
        sosiRepository.save(new Sosi("윤아"));
        sosiRepository.save(new Sosi("수영"));
        sosiRepository.save(new Sosi("효연"));
        sosiRepository.save(new Sosi("유리"));
        sosiRepository.save(new Sosi("티파니"));
        sosiRepository.save(new Sosi("써니"));
        sosiRepository.save(new Sosi("서현"));
    }
}


6. 실행

소녀시대 정보 가져오기
http://localhost:8080/sosi/1

스케줄 추가하기
http://localhost:8080/schedule/add/1?program=무한도전

다시 소녀시대정보 가져오기
http://localhost:8080/sosi/1


예제 소스는 깃헙에...
https://github.com/mudchobo/sosi-schedule-sb

 
Posted by 머드초보
,