React를 SpringBoot에서 서버사이드 랜더링하기 #1

최근 React Korea 커뮤니티에서 Spring과 React를 접목하려는 분들이 많아서 개인적으로 왜 저런 조합을 가져갈까 의아해 했는데 그 이유를 이제야 알았다. 첫번째 이유는 우리나라에 Java 개발자가 많다는 것이고, 두번째는 그 개발자들이 주로 서버 프로젝트로 Spring을 쓰기 때문이고, 그래서 많은 회사들은 Spring을 레거시로 들고 있을테고, 개발자는 여전히 힘들고, React는 뭔가 쉬울것 같고, 그러다보니 기존 레거시에 한번 적용해보고 싶은 욕망은 당여한거고, 막상해보니 어렵고,… 뭐 이런 흐름과 생각이 존재한다는 것을 최근에 복직하고서 알게 됐다. 아무튼 그래서 이번 포스트는 그런 개발자들을 수고를 좀 줄여보고자 정리해봤다.

참고로 나는 Java를 jdk 1.6이 막 나오던 시절에 쓴게 마지막이고 Spring 2.0이 막 나오던 시절에 설정에 치여서 접었던 경험이 있다. 한마디로 Java와 Spring은 내 전문 분야가 아니다. 따라서 뭔가 친절하고 디테일한 설명이 없음을 미리 말해둔다.

0. 삽질을 막기 위한 안내

최신 트렌드에 맞게 일단 모든걸 최신버전으로 해봤다. 따라서 아래 조건이 아니면 안될수도 있음을 미리 말해둔다. React가 IE8 이하 버전은 사실상 버렸기 때문에 웹프로젝트로 React를 쓰려는 사람은 일단 말리고 싶다. 굳이 또 그 어려운 길을 걷겠다면 말리진않겠다. 참고로 IE8을 쓰려면 React 1.4 버전을 써야하고 ES3를 위해 몇가지 pollyfil을 해야하니 알아서들 잘 찾아보시라. IE8도 지원안한다면서 이게 쓸만한건가? 라는 생각이 들수도 있을텐데 다행히 모바일웹에는 IE가 없기 때문에 모바일웹만 서비스한다면 괜찮은 조합이 아니라 아주 훌륭한 조합니다.

  • SpringBoot + JSP + Gradle
  • JDK 1.8 (Java8 nashorn Javascript Engine)
  • React 1.5
  • Webpack

1. SpringBoot 시작하기

SpringBoot는 스프링 이니셜라이져라는 사이트에서 설정을 스켓폴딩했다. 이 사이트에서 디펜던시로 ‘Web’을 추가하고 다운로드받자. 그리고 압축을 풀어서 인텔리J로 연다. 그럼 인텔리J가 빌드를 뭘로 할꺼냐고 묻는다. 나는 Gradle로 선택했다.

2. SpringBoot 서버 설정하기

다음으로 인텔리J에서 서버를 추가하자.

서버는 SpringBoot를 선택한다.

Name과 Main class 그리고 Use classpath of modthod를 지정한다. 마지막으로 JDK는 1.8로 지정한다. Java8에는 크롬V8 자바스크립트 엔진과 호환되는 nashorn(나스호른, 코뿔소)이라는 자바스크립트 엔진이 들어가 있다. 서버사이드 랜더링을 위해 Java8에 기본 탑재된 자바스크립트 엔진을 이용할 예정이기 때문에 1.8이 필요하다. 물론 JDK 1.7 이하 버전에서는 rhino(라이노, 예도 코뿔소)라는 자바스크립트 엔진이 들어가 있는데 라이노 최신버전도 ES5까지는 지원한다고하니까 될수도 있지 싶은데 테스트는 안해봤다.

서버를 설정한 뒤에 실행해서 http://localhost:8080 에서 확인해보자. 여기까지 안되면 다음을 진행을 못한다.

3. JSP 로드하기

2번까지 됐다는 전제하에 이제는 JSP 로드를 위해 추가 설정을 해야한다. src/main/resources/application.properties 파일을 열어서 JSP 설정을 추가한다.

spring.mvc.view.prefix: /WEB-INF/jsp/  
spring.mvc.view.suffix: .jsp

다음으로 build.gradle 파일을 열어서 JSP를 불러올수있는 서블릿 모듈과 jasper 모듈을 추가한다.

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    compile("javax.servlet:jstl")
    compile("org.apache.tomcat.embed:tomcat-embed-jasper")
}

그리고 잊지말고 꼭 그래들 빌드를 실행하자! 기본 SpringBoot 라이브러리에는 jasper가 기본으로 탑재되어 있지 않기 때문에 빌드를 실행해서 라이브러리를 땡겨오지 않으면 jsp 인식이 안된다. 요것때메 몇시간 삽질한 아픔이….OTL….

오늘은 여기까지
2편에서는 별도의 프로젝트로 리액트 샘플을 만들어본다.

루비(Ruby)와 PostgreSQL 설치 및 사용법 정리

필요에 의한 거지만 어찌됐든 10년만에 Ruby를 다시 배워보려고한다. 그래서 일단 맥에 루비부터 설치했다.

1. Homebrew를 이용한 루비 설치

일단 맥에는 루비가 기본 설치되어있기 때문에 기본 설치된 버전부터 확인해보자.

$> ruby -v
ruby 2.0 ..... 어쩌구 저쩌구 

내 멕에는 ruby 2.0이 설치되어 있다. 현재 최신버전은 루비 2.3.0 버전이다. 최신 맥프레임에도 불구하고 확실히 기본 설치된 버전은 낮다 -_-;; ㅎㅎㅎ 여튼 새버전을 선호하는 개인 취향문제로 새 버전을 설치했다.

$> brew install ruby
... 설치 로그 생략
$> ruby -v
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]

오예! 루비 새버전 장착 완료!! 루비로 웹개발을 할때는 보통 레일스 프레임워크를 사용하지만 지금 루비 문법과 스타일에도 익숙치 않은데 레일스를 볼 겨를이 없다. 일단 루비부터 익혀보자.

공식 언어 튜토리얼

  1. try Ruby – http://tryruby.org/
  2. 20분 가이드 – https://www.ruby-lang.org/ko/documentation/quickstart/

낮은 버전으로 다운그레이드

Homebrew를 이용해 낮은 버전으로 설치하려면 PATH 설정을 수동으로 해야한다. 홈브루 정책이 그런듯 싶다. 그래서 좀 귀찮다.

$> brew search ruby
chruby       homebrew/versions/ruby182    homebrew/versions/ruby21    ruby ✔
chruby-fish  homebrew/versions/ruby186    homebrew/versions/ruby22    ruby-build
homebrew/versions/imagemagick-ruby186     homebrew/versions/ruby187   imessage-ruby
ruby-install homebrew/versions/jruby168   homebrew/versions/ruby192   jruby
homebrew/versions/jruby1721               homebrew/versions/ruby193   mruby
...

현재 홈브루로 설치된 버전은 체크(✔)표시가 되어 있다. 만약 ruby 2.2 버전을 설치하고 싶다면 homebrew/versions/ruby22 버전을 설치하면된다 .

$> brew install homebrew/versions/ruby22
... 설치 로그 중략 ...
By default, binaries installed by gem will be placed into:
/usr/local/lib/ruby/gems/2.2.0/bin

You may want to add this to your PATH.
... 

이전 버전을 설치하면 위 메시지처럼 PATH를 니들이 알아서 해! 라는 배째라 모드.. 아무래도 루비가 자체 패키지매니저(gem)을 가지고 있어서 의존성을 가진 모듈들이 충돌될까바 그런듯싶고,.. 여튼 귀찮아서 그냥 새버전 쓰는걸로!!

2. PostgreSQL 설치와 사용법

PostgreSQL 설치도 Homebrew를 이용하면 간단하다.

$> brew install postgresql

DB 서버를 데몬으로 항상 띄워둘수도 있지만 본인은 개발할때 수동으로 실행시키는 편이다.

$> postgres -D /usr/local/var/postgres

서버는 실행했고 이제부터가 진짜 신세계다! mysql과 mongo와는 또다른 명령체계들 때문에 삽질했다. 정리해본다.

1. 클라이언트 접속툴

postgresql은 생소한 psql이라는 명령어를 이용한다. 하기사 데이터베이스 이름 자체가 길다보니 짧은 명령어를 만든듯 싶다.

$> psql
psql: FATAL:  database "realrap" does not exist

잉? 이건 뭔소리래? “realrap” 데이터베이스가 없다며 접속되지 않는다. psql은 다른 데이터베이스와 좀 다르게 일단 접속하고 use 명령을 이용해 데이터베이스를 선택하는 모델이 아닌갑다. 그래서 접속할때 데이터베이스명이 필요한데 지정하지 않으면 로그인된 계정이름이 디폴트로 지정된다. 참고로 내 계정이름이 “realrap” 이다.

데이터베이스를 생성하기 전에 샘플디비(template1)로 접속부터해보자.

$> psql template1
psql (9.5.1)
Type "help" for help.

template1=#

특이하게 접속하면 template1=# 이라는 프로프트를 볼수있다. template1 데이터베이스에 접속했다는 의미다. 그리고 template0 이라는 샘플디비가 하나 더 있는데 진짜 있는지 데이터베이스 목록을 확인해보자. 역슬래쉬() 소문자 엘(l)을 입력해보자.

template1=# \l
                                       List of databases
      Name      |  Owner  | Encoding |   Collate   |    Ctype    |  Access privileges
----------------+---------+----------+-------------+-------------+---------------------
 postgres       | realrap | UTF8     | ko_KR.UTF-8 | ko_KR.UTF-8 |
 template0      | realrap | UTF8     | ko_KR.UTF-8 | ko_KR.UTF-8 | =c/realrap         +
                |         |          |             |             | realrap=CTc/realrap
 template1      | realrap | UTF8     | ko_KR.UTF-8 | ko_KR.UTF-8 | =c/realrap         +
                |         |          |             |             | realrap=CTc/realrap
 (3 rows)

나도 모르는 명령어가 툭툭튀어나온다. 이런 명령어는 help 명령을 이용해 알아냈다.

template1=# help
You are using psql, the command-line interface to PostgreSQL.
Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit

나가려면 \q를 입력하고 psql 명령어를 보고 싶으면 \? 물음표를 입력하렌다. 물음표를 입력해보면 \l 명령어에 대한 설명도 볼수있다.

2. 데이터 베이스 생성

데이터 베이스 생성은 createdb 명령을 이용한다. 주의할점은 데이터베이스명을 따로 지정하지 않으면 현재 로그인 된 계정 이름으로 생성한다. 자그럼 생성해보자!

createdb <데이터베이스명>

$> createdb
$> 

잉? 이건 또 뭐징? 아무런 메시지가 없다. 제대로 실행 된건가? 약간 의아한데 확인해봐야겠다. 그리고 뭔가 로그를 출력해주는 설정이 있을것 같은데 모르겠다. 일단 아까 말했던것처럼 데이터베이스명을 따로 지정하지 않으면 현재 로그인한 계정으로 만들어진다. 진짜 만들어졌는지 접속해서 확인해보자 .

$> psql realrap
psql (9.5.1)
Type "help" for help.

realrap=# \l
                                       List of databases
      Name      |  Owner  | Encoding |   Collate   |    Ctype    |  Access privileges
----------------+---------+----------+-------------+-------------+---------------------
 postgres       | realrap | UTF8     | ko_KR.UTF-8 | ko_KR.UTF-8 |
 realrap        | realrap | UTF8     | ko_KR.UTF-8 | ko_KR.UTF-8 |
....
.... 중략 ....

우잉? 진짜된다. 헐퀴.. realrap 이름으로 만든 디비는 필요 없으므로 지우자. 지우는 명령어는 dropdb 다! 일단 디비를 빠져나와서 커맨트창에 아래와 같이 입력한다.

$> dropdb realrap
$> psql realrap
psql: FATAL:  database "realrap" does not exist

됐다. 제대로 지워졌는지 접속이 되지 않는다. 대충 사용법을 알았으니 개발용 데이터베이스(test)와 개발용 유저(dev)를 만들어 dev 유저가 이 test 데이터베이스를 쓸수있도록 권한을 줘보자.

3. 개발용 유저 생성

일단 유저를 만든다. 만들때 –interactive 명령을 주면 좀더 상세한 권한 설정을 할수있다.

$> createuser dev 
or
$> createuser dev --interactive
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) n
Shall the new role be allowed to create more new roles? (y/n) n

상세 설정을 통해 권한을 따로 주지 않으면 유저는 기본적으로 아무런 권한이 없다.

4. 개발용 데이터베이스 생성

testDB를 만들때 -O 옵션을 주면 데이터베이스 오너를 지정할수있다. 앞에서 생성한 dev 유저가 testDB 오너가 되도록 생성한다.

$> createdb -O dev testDB

제대로 설정됐는지 로컬 계정으로 testDB에 접근해서 설정을 확인해보자.

$> psql testDB
psql (9.5.1)
Type "help" for help.

testDB=# \l
                               List of databases
      Name      |  Owner  | Encoding |   Collate   |    Ctype    |  Access privileges
----------------+---------+----------+-------------+-------------+---------------------
... 중략 ...
 testDB         | dev     | UTF8     | ko_KR.UTF-8 | ko_KR.UTF-8 |
... 중략 ... 

testDB의 Owner가 dev로 지정되어 있다. 제대로 했다. 이제 dev 유저에 패스워드를 지정해보자.

testDB=# alter user dev with password 'devpass';

dev 계정에 ‘devpass’ 라는 비밀번호를 부여했다. 이제 빠져나와서 dev 계정으로 접속해보자.

$> psql -U dev test -W
Password for user dev:

그런데 여기서 멘붕!…--;;; 패스워드가 틀려도 접속이 된다. 머징? –-;;; 좀더 공부를 해야겠다. 일단 여기까지… -_-;;

5. PostgreSQL 데이터베이스 지우기

멘붕에 빠져서 아까 만들었던 데이터베이스와 더불어 postgresql도 다 지워야겠다. 지우는 방법은 간단하다.

$> brew uninstall postgresql 

이렇게 지우면 명령어만 지우는거고 실제로 데이터베이스를 살아있다. 그래서 다시 설치하면 기존에 만들었던 유저라든가 데이터베이스가 지저분하게 그대로 있다. 따라서 데이터베이스까지 지우려면 저장된 DB 파일도 지워야한다.

일단 homebrew가 어디에 DB를 두는지 확인해야한다.

$> ps aux | grep postgres | grep -- -D
realrap         46604   0.0  0.1  2603388  15392 s006  S+   11:19AM   0:00.25 postgres -D /usr/local/var/postgres

아하! /user/local/var/postgres 폴더에 저장하는구나. 이 폴더를 날리면 DB도 깨끗이 지워진다.

6. GUI 툴을 이용해 PostgreSQL 다루기

앱스토어에가서 ‘Valentina’를 검색후 설치한다. 맥이 아는 경우는 아래 링크를 참고한다.
http://www.valentina-db.com/en/valentina-studio-overview

백그라운드에서 postgresSQL을 떠 있다면 Valentina 앱에서 자동으로 인식한다. 커넥터 설정을 할때 유의할점은 앞에서 살펴본 바와 같이 본인의 계정이름으로 사용자가 자동 설정된다는 것이다. 따라서 설치후 별다른 유저나 비밀번호 설정을 안했다면 로컬 접속은 아래와 같이 입력한다.

Host: localhost
Database: [생성한 데이터베이스 이름]
User: [본인 계정 이름] 
Password: *중요! 따로 설정하지 않았다면 비워둔다!!

Parse.com 마이그레이션 #1

오늘 Parse.com으로 부터 아래와 같은 메일을 한장 받았다.

“We’re happy to announce that we’ve partnered with MongoLab and ObjectRocket to give you a discount on database hosting. Both companies have deep experience in helping Parse customers migrate to a fully managed MongoDB solution, and are the perfect partner for scaling your app. You’ll need to act fast, as both these discounts are only valid if you start in the next 30 days.

내용인 즉슨 Parse 서버에 있는 데이터를 MongoLab 이나 ObjectRocket 클라우드 서비스로 마이그레이션을 하라는 얘기였다. 그리고 지금부터 한달안에 마이그레이션하면 디스카운트 해준다고… ㅎㅎㅎ 나는 이 디스카운트에 낚여서 오늘 본의 아니게 미뤄뒀던 알리브로 마이그레이션 작업을 하게됐다.

Parse.com 서버 마이그레이션

마이그레이션에 대한 자세한 내용은 공식 가이드 문서를 참고하면 되는데 사실 영어라 눈에 잘 들어오지도 않고 처음 파스닷컴 문 닫는다고 발표했을때 살짝쿵 멘붕이라 주의깊게 읽어보지도 않았다. 좌우지간 오늘은 가이드에서 얘기하는 첫번째 스탭을 밟아보겠다.

1. DB 마이그레이션를 위한 준비

공식 가이드 문서를 열어보면 제일 먼저 눈에 들어오는게 아래와 같은 그림이다.

enter image description here

여기서 파스닷컴의 현재상황은 Step 0으로 표현된다. 무슨 말인고하니 파스닷컴은 API 서버 역할 뿐만 아니라 “Parse Hosted DB” 라고 표시된 DB 서버를 가지고 있었다는 얘기다. 그래서 첫번째 DB 마이그레이션 단계가 바로 이 파스닷컴에서 서비스 해주던 DB를 딴데로 옮기라는 뜻이다.

그래서 처음엔 어디로 옮겨야되나? 막막했다. 막막함에 일단 그림에 나와 있는데로 아무생각없이 내 로컬 PC에 MongoDB를 설치했다. 그리고 파스닷컴의 API 서버가 내 몽고디비를 바라보도록 하면 되는건가? 싶었는데, 그러면 내 로컬PC가 항상 켜져있어야된다. 어랏! 그럼 안되지!! 처음 가이드 문서를 대충읽었을때 여기서 막혀버렸다. 도대체 어쩌라는 건지.. ㅎㅎㅎ

오늘 다시 읽어보니 그림에서 “Self Hosted” DB는 로컬에 MongoDB를 설치하라는 얘기가 아니라 파스닷컴에서 서비스하던 데이터를 니가 책임져라! 라는 뜻이었다. 즉, 몽고랩이나 오브젝트로켓 같은 클라우드 서비스로 옮기라는 뜻이다. 파스닷컴은 몽고랩이 샌드박스 하나는 공짜니까 몽고랩을 추천한것 같다. 여튼 2~3년전에 한번 가입한거 같았는데 계정을 잃어버려서 새로 만들었다.

2 몽고랩(MongoLab) 계정 만들기

몽고랩은 500MB 짜리 샌드박스 하나를 무료로 만들수있다. 이 샌드박스에 데이터베이스를 만들고 Users 탭에서 유저를 하나 생성한다. 그러면 아래와 같은 명령어로 몽고랩 DB 서버에 접속해서 Shell 명령어로 조회를 해볼수있다. 참고로 내가 만든 데이터베이스 이름은 rlibro 다.

$> mongo ds015878.mongolab.com:15878/rlibro -u <dbuser> -p <dbpassword>

쉘로 접속할때는 위와 같은 명령어를 쓰지만 프로그램에서 데이터를 조작할때는 아래와 같은 MongoDB URI를 이용한다.

$> mongodb://<dbuser>:<dbpassword>@ds015878.mongolab.com:15878/rlibro

3. 파스닷컴(Parse.com) DB를 몽고랩에 마이그레이션하기

이제 가이드 문서의 그림처럼 Step1을 진행해보자. 파스닷컴 대쉬보드로 가서 App Settings > General 메뉴에서 Migrate 버튼을 클릭한다.
enter image description here

그림처럼 몽고랩 URI를 넣어주고 마이그레이션을 시작하면 파스닷컴의 모든 데이터를 몽고랩 DB로 싱크시켜준다. 그리고 Finalize 버튼을 클릭하면 더이상 파스닷컴 서버의 DB를 사용하지 않고 몽고랩 서버의 DB를 사용하게 된다. 즉 아래 그림처럼 된다.

enter image description here

여기까지가 파스닷컴에서 메일로 노티까지 해가면 마이그레이션하라는 첫번째 단계가 되겠다. 다음 단계는 파스닷컴의 API 서버를 대체해줄 Parse-Server를 로컬에 설치해서 몽고랩 DB와 연결하는 단계다. 이건 다음에 정리~!!

참고 및 이미지 출처

  1. https://github.com/ParsePlatform/parse-server/wiki/Migrating-an-Existing-Parse-App
  2. https://learnappmaking.com/how-to-migrate-parse-app-parse-server-heroku-mongolab/

안드로이드 앱 디컴파일 해보기

의도한건 아닌데 하다보니 졸지에 안드로이드 앱을 디컴파일까지 하게됐다. 오늘 한일을 정리해보자.

1. 맥에 안드로이드 SDK 설치하기

일단 안드로이드 SDK부터 설치하자. 전에는 직접 다운로드 받아서 ~/dev 폴더에 넣고 관리했었는데, HomeBrew에도 android-sdk 모듈이 있었다!. 난 홈브루 매니아니까 홈브루로 고고!!

$> brew install android-sdk
$> brew info android-sdk
android-sdk: stable 24.3.3
Android API libraries and developer tools
https://developer.android.com/index.html
... 
You may need to add the following to your .bashrc:
export ANDROID_HOME=/usr/local/opt/android-sdk
...

설치된 정보를 보니 24.3.3 버전이다. 그리고 ~/.profile 파일을 열어서 아래와 같이 ANDROID_HOME과 필요한 PATH를 잡아준다.

// .profile 
export ANDROID_HOME=/usr/local/opt/android-sdk
export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools 

2. 자바 최신 버전 설치하기

다음으로 해야할 일은 자바 최신버전을 까는 일이다. 아마 맥에 자바를 따로 설치하지 않았다면 옛날버전일 것이다.

$> java -version
java version "1.6.0_65"

역시나 오랜된 1.6 버전이다. 혹시나해서 홈브루엔 자바가 없나? 했는데 역시나 없다. 그냥 오라클 사이트가서 다운로드 받아 설치한다. 설치가 끝났다면 확인해보자.

$> java -version
java version "1.8.0_60"

올레~!! 됐네 됐어!! 그나저나 난 왜 자바 최신버전까지 깐거지? 그건 뒤 이어 설치할 디컴파일 툴들이 자바 1.7이상에서 돌기 때문이다. 이왕 설치한김에 최신버전~ ㅎㅎ 필요에 따라서는 JAVA_HOME을 설정해야될지도 모르겠다. Mac에서 자바홈은 아래 위치에 있다고 하는데 맥에선 패키지로 인스톨하다보니 JAVA_HOME을 딱히 안해도 되더라~ 오히려 했다가 패스가 안잡혀서 오류가 났었다.

export JAVA_HOME=/usr/libexec/java_home

실제 맥에 JDK가 설치되는 위치는 아래와 같다. 자세한 내용은 문서를 참고하자.

/Library/Java/JavaVirtualMachines/jdk<major>.<minor>.<macro[_update]>.jdk

3. 디컴파일 툴 설치하기

디컴파일 툴은 2가지가 있다. 참고링크: http://visu4l.tistory.com/412

  • dex2jar : https://code.google.com/p/dex2jar/
  • JD-GUI : http://jd.benow.ca/

dex2jar 파일은 apk 파일을 디컴파일해서 jar 파일로 변환해준다. 그리고 JD-GUI는 앞에서 변환한 jar 파일을 열어서 실제 디컴파일된 소스를 볼때 유용하다.

4. 폰에 설치된 앱 가져오기

필요한 도구는 모두 갖춰놨으니 이제 실제 폰에 있는 안드로이드 앱을 가져다가 디컴파일해보자! 일단 폰에 있는 apk 파일을 가져오려면 폰을 USB로 맥에 연결하고 개발자 옵션을 켜야한다. 일단 여기까지 했으면 폰이 제대로 PC에 연결됐는지 확인해보자. 이때 adb 라는 디버깅 도구를 이용한다.

$> adb devices
List of devices attached
d22849b7    device

그런데 adb가 없을수도 있다. 왜냐면 디버깅 도구는 기본 SDK에 포함되어 있지 않고 별도로 설치해야한다. 뭐 간단히 명령어 한줄이면 된다.

$> android update sdk --no-ui --filter 'platform-tools'

여튼 adb 명령어는 매우 유용한다. 연결된 폰이 하나라면 바로 쉡(shell)로 접속할수도 있다.

$> adb shell
shell@ks01lteskt:/ $ ls -al
drwxr-xr-x root     root              1970-02-06 11:46 .system
drwxr-xr-x root     root              1970-02-06 11:46 acct
drwxrwx--- system   cache             2015-09-01 20:29 cache
dr-x------ root     root              1970-02-06 11:46 config
lrwxrwxrwx root     root              1970-02-06 11:46 d -> /sys/kernel/debug
drwxrwx--x system   system            1970-02-06 11:46 data
-rw-r--r-- root     root          255 1970-01-01 09:00 default.prop
drwxr-xr-x root     root              2015-07-29 16:34 dev
...
..

오호라! 굉장한데? 하면서 막 폰속을 서핑하지만 정작 중요한 순간에는 퍼미션이 없다고 나온다. 특히 설치된 앱들이 모여있는 /data/app 폴더는 볼수가 없다.

shell@ks01lteskt:/ $ ls -al /data/app
opendir failed, Permission denied

아… 어쩌지?… 어쩌지… 라고 생각하면서 또 잽싸게 써핑해보니, 역시나 있다 있어! 방법이 다 있어!!

설치된 팩키지 목록

일단 설치된 폴더의 파일 목록을 볼수가 없으니 꼼수를 이용해야한다. 먼저 설치된 패키지가 뭐가 있는지 검색해보자.

$> adb shell pm list packages
adb shell pm list packages
...
package:com.sec.android.app.mediasync
package:com.sec.android.app.sbrowsertry
package:com.sec.android.app.controlpanel
package:com.android.printspooler
package:com.google.android.syncadapters.calendar
... 
..

그런데 오지게 많이 나온다. 일일이 확인하기 어려우니까 적당히 필터하면서 원하는 앱의 패키지 명을 찾는다.

$> adb shell pm list packages | grep <필터할 글자>

설치된 패키지의 실제 경로 확인

그리고 원하는 앱의 패키지명을 찾았다면 그 앱의 실제 경로를 확인해본다. 예를 들어 폰에 멜론앱이 설치되어 있다면 아래와 같이 실제 경로를 확인할수있다.

$> adb shell pm path com.iloen.melon
package:/data/app/com.iloen.melon-7.apk

앱에 있는 apk 파일 다운로드

이제 PC로 다운로드하자!!

$> adb pull /data/app/com.iloen.melon-7.apk

이렇게 추출해낸 apk 파일은 zip으로 압축된 녀석이라 그냥 확장자만 바꿔서 압축을 풀어도 내용을 볼수있지만 실제 소스코드는 컴파일되어 있기 때문에 보기 어렵다. 이제 이 .apk 파일을 디컴파일해보자. 필요한 도구는 앞에서 다 설치해놨다.

5. 소스코드 디컴파일

디컴파일 방법은 또 쓰기 귀찮으니까 링크를 참고하자! http://visu4l.tistory.com/412

다른점이 있다면 도구들이 버전업이 되서 이름이 바뀐것 밖에 없다. 맥에서 할라면 당연히 .bat 파일이 아니라 .sh 파일을 실행시켜야된다. 뭐 이런식이다.

$> sh d2j-dex2jar.sh com.iloen.melon-7.apk

자바 버전이 1.7이상이라면 특별한 문제없이 잘 동작한다. 결과물은 파일명 뒤에 -dex2jar.jar 라는 접미사가 붙는다.

마지막으로 JD-GUI를 실행시켜서 위 파일을 열어서 확인한다. 대부분의 소스코드가 디컴파일되서 java 파일을 볼수있다. 그런데 안드로이드는 java 파일만있다고 해석할수있는게 아니다.
AndroidManifest.xml 파일도 같이 봐야한다.

6. XML 디컴파일

xml 파일을 디컴파일 하려면 apktool을 설치해야한다. 설치는 알아서들.. 점점 귀찮아진다.

http://ibotpeaches.github.io/Apktool/install/

사용법은 이렇다.

$> apktool d com.iloen.melon-7.apk

덧,

사실은 이 모든 것들이 Appium 프로젝트를 만들다가 벌어진일이다.
젠장알~~ -_-;.. 뭐한거냐?
아직 시작도 못했다. ㅎㅎ

썸머노트에서 이미지 업로드 할때 흔히 발견되는 nodejs 에러들

썸머노트에서 이미지 업로드하기

썸머노트에서 이미지를 서버에 올리려면 일단 아래와 같이 별도의 onImageUpload 핸들러를 구현해야한다.

var $editor = $('#editor');
var editor = $editor.summernote({
 ... 옵션 생략 ... 
 onImageUpload: function(files) {
    sendFile(files[0]);
  }
});

위에서 사용한 sendFile 함수는 아래와 같이 구현한다. 이때 ajax 옵션에 주의하자. 별거아닌 옵션을 실수로 잘못주면 하루종일 이유도 모르고 헤맬수있다. 특히 contentType은 없어야한다. 파일 업로드라고 종종 contentType을 “multipart/form-data”로 설정하는 경우가 있는데, 본래 XMLHttpRequest 객체는 파일 업로드를 지원하지 않기 때문에 파일 업로드를 위해서는 XHR2와 FormData 객체를 이용해야한다. 참고로 XHR2는 거의 모든 최신 브라우저에서 사용할수있다.

function sendFile(file) {
  var data = new FormData();
  data.append("file", file);

  $.ajax({
    type: "POST",
    url: "/api/upload/image",
    type: 'POST',
    data: data,
    contentType: false,
    processData: false, // Don't process the files
    success: function(data) {
      $('#editor').summernote("insertImage", data.url);
    }
  });
}

간혹 no multipart boundary was found 라는 오류가 발생하면 바로 컨텐츠 타입을 의심해보면 된다.

업로드 서버 구현하기

이제 파일을 업로드를 해줄 서버를 구현해보자. 노드로 구현할때 나는 주로 쓰는 multer라는 모듈을 사용한다. 사용법 또한 무지 간단하다.

var router = express.Router();
var multer = require('multer');
var upload = multer({ 
  dest: 'uploads/'
});
router.post('/upload/image', upload.single('file'), function(req, res){
  console.log(req.file);
});

하지만 간단하다고 생각할때 아래와 같은 에러를 만나게 된다.

위 두 에러는 ajax로 파일을 업로드할때 파일내용을 request body에 써버 보내게 되는데 바로 그 바디의 크기 제한에 걸려나 나는 문제다. 이럴땐 당황하지 말고 아래와 같이 설정한다.

app.use(bodyParser.urlencoded({limit: '5mb', extended: false, parameterLimit: 10000}));

물론 node 앞딴에 Nginx 같은 스태틱 서버를 두는 경우엔 nginx 설정에도 업로드 사이즈를 설정해야한다.
관련글은 검색하면 쉽게 찾을수있다.

http {
#...
    client_max_body_size 100m;
#...
}