NGINX + WORDPRESS + 멀티페이지 설치방법
1. wp-config.php 파일 설정

define('WP_ALLOW_MULTISITE', true);

2. wp관리자 로그인 - 도구 - 네트워크 설정 중 wp-config.php 파일 내 수정코드 수정

define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', true);
define('DOMAIN_CURRENT_SITE', '');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1); 

 * 반드시 1번 코드를 삭제해주어야함.. 이것도 모르고 계속 같이 반영했다가 삽질..

/------- 참고 -------/


WordPress's multisite feature offers the ability to create multiple websites from a single installation of WordPress. Each site can have a separate theme, set of plugins, and collection of content (posts and pages). This reduces the overhead of maintaining and updating several installations of WordPress, while allowing you to host multiple sites which are totally unrelated to one another.

WordPress multisite comes in two flavours: subdirectory or subdomain. In this tutorial, we will set up WordPress multisite to use subdomains. That means that sites we create will have a subdomain web address like This can be mapped to an external domain like so that each site looks independent from the outside.


This tutorial requires the user to have a knowledge of the following:

We will be creating three WordPress sites with the following domain names:

  • Site 1:

    Domain: (Primary domain)

    This is the site that is created when WordPress is installed.

  • Site 2:

    External Domain:


  • Site 3:

    External Domain:


The first domain is the primary domain name through which WordPress will be installed. Make sure to set up DNS for all three domains to point to the IP address of the Droplet which will host WordPress.

Step One - Set Up DNS Wildcard Records

In this section, we will add a DNS wildcard record for the primary domain so that more sites can be added at any time, without needing individual A records. (Alternately, you can add a new A record for each subdomain.)

Note: This has to be done only for the *primary domain (examplewp.comin this tutorial).

Log in to your DigitalOcean control panel and navigate to the Networking section. Edit the primary domain and create a wildcard A record for this domain pointing to the Droplet's IP address. A wildcard record is created by entering an asterisk (*) in the hostname input box as shown in the screenshot below.

DNS Control Panel - wildcard record

If you host your domain's DNS elsewhere, you should set the wildcard record there instead.

What you should see now:

DNS queries for any should return the IP address of your Droplet.

Step Two - Install and Configure the LEMP Stack

In this section, we will install and configure Nginx, MySQL, and PHP. There is a detailed article on setting up a LEMP stack that you can reference if you would like to. This section will serve as a quick setup. There is also a LEMP on Ubuntu 14.04 image under the Applications tab in the Select Image section when creating a Droplet.

Update the repositories and install Nginx, MySQL, PHP5-FPM and other necessary PHP modules.

apt-get update
apt-get install -y nginx mysql-server php5-fpm php5-mysql php5-curl php5-mcrypt php5-gd

When MySQL server is being installed, you will be prompted to enter a password for the root database user. Please enter a strong password and do not leave it blank. You will enter the password twice.

MySQL root password

Create a document root for Nginx which will hold the WordPress files. We will use /usr/share/nginx/wordpress throughout this tutorial.

mkdir /usr/share/nginx/wordpress

We will replace Nginx's default virtual host with our own, so remove its symlink in the sites-enableddirectory.

rm /etc/nginx/sites-enabled/default

Create a new virtual host file inside the sites-available directory. This file can be named anything. In our example, we will call it wp-ms.

nano /etc/nginx/sites-available/wp-ms

Edit this file and place the following configuration. Edit the text highlighted in red according to your environment. In the server_name line, you should add all three (or more) of your multisite domains, and the wildcard subdomain for the first domain.

server {
    listen [::]:80 ipv6only=off;
    server_name *;

    root /usr/share/nginx/wordpress;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$args ;

    location ~ /favicon.ico {
        access_log off;
        log_not_found off;

    location ~ \.php$ {
        try_files $uri /index.php;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;

    access_log  /var/log/nginx/$host-access.log;
    error_log   /var/log/nginx/wpms-error.log;

If this Droplet will host only this WordPress installation, the listen and server_name directives can be changed to the following:

listen [::]:80 default_server ipv6only=off;
server_name *;

The use of the $host variable in the access_log directive creates separate log files for each domain like and It is not possible to use such variables for the error_log directive, so all errors are logged in a single file.

Save this file and create a symlink of this file inside the sites-enabled directory.

ln -s /etc/nginx/sites-available/wp-ms /etc/nginx/sites-enabled/wp-ms

Execute an Nginx configuration test and restart if it returns OK.

service nginx configtest
service nginx restart

Step Three - Create a MySQL Database and User for WordPress

In this section, we will create a MySQL database for WordPress and a user with permissions for this database only.

Log in to the MySQL command line as the root user.

mysql -u root -p

Create a database.


Create a MySQL user and grant permissions to this database:

CREATE USER 'wordpress_user'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress_user'@'localhost';

Replace the word password with a strong password. Flush the privileges and exit the MySQL command line.


Note down these details as will be needing them in Step Four.

Database Name: wordpress

Database User: wordpress_user

Database Password: password

More information about MySQL users can be found in this article.

Step Four - Download and Install WordPress

In this section, we will download the latest version of WordPress and install it. This will be for our first site,

Download and extract WordPress.

tar -xf latest.tar.gz

Move the extracted files into the document root.

mv wordpress/* /usr/share/nginx/wordpress/

Assign ownership to the user www-data. This is essential for media uploads and for core/plugin/theme updates to work in WordPress.

chown -R www-data:www-data /usr/share/nginx/wordpress

Access the primary domain in your browser to begin the WordPress installation.

You can add the "www" suffix to the URL if needed. Click the Create a Configuration File button followed by the Let's go! button. Fill in the database details (use the information from Step Three) and click Submit.

WordPress database details

At this point, WordPress will establish a connection with the database to test the entered credentials. Once the connection succeeds, the Run the install button appears. Click on it. Complete the Information needed form to set up your site title, username, password, and email, and then click Install WordPress. It is recommended to choose a non-generic username for security.

WordPress welcome screen

Step Five - Enable Multisite and Create Additional Sites

In this section, we will enable WordPress Multisite and create the two additional sites mentioned in the Prequisites section of this article.

PHP constant has to be defined in the wp-config.php file to enable the Network Setup page.

Edit the wp-config.php file:

nano /usr/share/nginx/wordpress/wp-config.php

Add the following code before the comment /* That's all, stop editing! Happy blogging. */:

/* Multisite settings */
define( 'WP_ALLOW_MULTISITE', true );

We will be editing this file a few more times during this tutorial. Feel free to add all of the new lines in the /* Multisite settings */ section we just created.

Save the file. Log in to the WordPress admin panel and navigate to Tools > Network Setup. Choose the Sub-domains option, modify the Network Title as desired, and then click Install.

WordPress Network Setup

You will be presented with two blocks of code to be added in the wp-config.php and .htaccess files. Copy the wp-config.php code which looks similar to the following:

define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', true);
define('DOMAIN_CURRENT_SITE', '');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);

Edit the wp-config.php file.

nano /usr/share/nginx/wordpress/wp-config.php

Add these lines before the comment /* That's all, stop editing! Happy blogging. */ and save it. The code displayed for .htaccess can be ignored, as Nginx does not have this file.

Log out of the WordPress admin panel, and log in again. From the admin toolbar on the top left, navigate to the My Sites > Network Admin > Sites.

WordPress Toolbar

Click the Add New button to open the Add New Site form. The following screenshot shows the filled-in details for the shopping site in our example. The Site Address entered will form the subdomain of this site.

Creating a new WordPress site

Click Add Site and the created site will be accessible via

Repeat these steps to create the second site ( in our example).

What you should see now:

The following three WordPress sites:


Each of them will have their own content, theme, and active set of plugins.

Step Six - Set Up Domain Mapping

In this section, we will enable you to use a separate domain name for each WordPress site, by downloading and enabling the WordPress MU Domain Mapping plugin. This plugin allows users of WordPress Multisite to map their blog/site to another domain.

Log in to your Droplet via SSH and download the WordPress MU Domain Mapping plugin. First install the unzip command, and then extract the plugin.

apt-get install unzip

Move the extracted files to the WordPress plugin directory.

mv wordpress-mu-domain-mapping /usr/share/nginx/wordpress/wp-content/plugins/

Copy the sunrise.php file from the plugin's directory to the wp-content directory.

cp /usr/share/nginx/wordpress/wp-content/plugins/wordpress-mu-domain-mapping/sunrise.php /usr/share/nginx/wordpress/wp-content/

Edit the wp-config.php file and add the following line before the comment /* That's all, stop editing! Happy blogging. */.

File: /usr/share/nginx/wordpress/wp-config.php

define('SUNRISE', 'on');

Save this file and return to the web browser. From the WordPress toolbar navigate to My Sites > Network Admin > Plugins.


Click the Network Activate link under the WordPress MU Domain Mapping plugin. Go to Settings > Domain Mapping and make changes to the Domain Options as follows:

  • Uncheck Remote Login
  • Check Permanent Redirect
  • Uncheck Redirect administration pages to site's original domain

Domain mapping options

Click Save once done. These settings redirect all requests for subdomains (like to their respective external domains (like including the administration pages (/wp-admin).

In the next step we will be mapping a domain name to each site based on its site ID. There are many ways to find the ID of a site but for easier administration we will create a simple WordPress Must-use plugin that displays an additional ID column on the Sites page.

Log in to your Droplet via SSH and create an mu-plugins directory.

mkdir /usr/share/nginx/wordpress/wp-content/mu-plugins

Create a PHP file inside this directory and paste the code that follows:

nano /usr/share/nginx/wordpress/wp-content/mu-plugins/wpms_blogid.php

You can copy this content exactly:

add_filter( 'wpmu_blogs_columns', 'do_get_id' );
add_action( 'manage_sites_custom_column', 'do_add_columns', 10, 2 );
add_action( 'manage_blogs_custom_column', 'do_add_columns', 10, 2 );

function do_add_columns( $column_name, $blog_id ) {
    if ( 'blog_id' === $column_name )
        echo $blog_id;
    return $column_name;

function do_get_id( $columns ) {
    $columns['blog_id'] = 'ID';
    return $columns;

The Sites > All Sites section should now show an additional ID column.


Note down the ID values for each site and go to the Settings > Domains page. Enter the site ID followed by the external domain for the site. For example, since companysite has an ID of 3, on this page, the Site ID should be 3, and the domain should be

Mapping a site ID to a domain

You may add a "www" prefix if you wish to set the site URL as Repeat these steps for the other domains. Click Save at the bottom of the page.

What you should see now:

Each site will have its own domain name instead of a subdomain; i.e., entering in your browser will open the My Online Company Site. You can check this now by visiting and You should see the site title change in the upper left corner of the page.

Now each site can be maintained separately through its own WordPress admin panel:

Updates to the core/plugins/themes and installation of plugins/themes should be done from the network admin page of the primary domain:

* 출처 :

Centos 환경에서만 가능

워드프레스 + 그누프레스 + NGINX 환경에서 구축 중

멀티페이지 설정 중 /wp/wp-config.php 파일 건들다가 로그인 후에도 로그인페이지로 계속 리다이렉트 되는 문제 발생하여 아래와같이 셋팅 후 이상없음

/* That's all, stop editing! Happy blogging. */

/*define('MULTISITE', true);*/

define('WP_ALLOW_MULTISITE', true);

define('DOMAIN_CURRENT_SITE', '');

define('PATH_CURRENT_SITE', '/wp/');

define('SITE_ID_CURRENT_SITE', 1);

define('BLOG_ID_CURRENT_SITE', 1);

1편 혼자서 법인등기에 이어, 법인인감카드 발급과 사업자등록 방법을 정리해봅니다.

1. 등기상태 확인하기

서울중앙지방법원 등기국에 서류를 내고 이틀 정도 있다가 인터넷 등기소 에 가서 ‘체커’라고 검색을 해보니, 등기가 완료되었더라고요.


2. 법인인감카드발급하기

법인은 인감증명서를 발급하기 위해서 카드가 필요하더군요. 법인인감카드발급신청서 를 작성해서 가까운 등기국에 방문합니다. ( 서초동 중앙지법 등기국말고 집 주위에 가까운 등기국에 가면 됩니다. )

저는 서울남부지방법원 등기국으로 가서 인감카드 발급 후 무인기에서 1부당 1,000원씩에 인감5부, 등본5부를 발급했습니다.

참고로 별생각 없이 목동에 서울남부지방법원에 등기국이 있겠거니 하고 갔는데, 작년 6월부터 등기국이 몇몇 지역이 통합돼서 구로세무서 옆으로 이사했더라고요. -_-; 서쪽에 사시는 분들은 괜히 목동 갔다가 헛걸음하지 마시고 구로세무서 옆으로 가세요~

3. 사업자등록하기

이제 인감과 등본을 발급받았으니 사업자등록을 하러 갑니다. 그전에, 사업자 등록에는 몇몇 서류가 필요한데요.

  • 법인사업자등록신청서 -> 이건 세무서가면 있습니다
  • 법인등기부등본 1본 (사본가능)
  • 법인인감증명서 1본 (사본가능)
  • 정관 사본 1부
  • 주주명부 사본 1부
  • 법인명의로 전환된 부동산임대차계약서 1부 (혹은 사업장 무상사용 승낙서)
  • 대표이사 신분증

저는 법인 명의 재계약 대신 사업장 무상사용 승낙서를 임대인에게 받았습니다.

서류가 전부 준비되었다면 서류를 들고 세무서로 갑니다~ 저는 강서 세무서로 갔습니다. 주차비도 안 받고, 사람도 거의 없어서 준비만 잘해가시면 금방 끝납니다. 저는 업태/종목을 찾느라 20분 정도 걸렸는데요, 가시기 전에 정관에 등록한 사업목적에 맞는 업태/종목을 미리 정리해서 가시면 금방 끝내실 수 있어요!


이거 한 장 받겠다고, 몇~일을 고생했습니다. ㅋㅋㅋ

이제 법인통장, 법인카드, 공인인증서 만들러 은행에 갑니다!

[출처 : ]

1. 상호결정

인터넷 등기소의 법인 상호 검색 서비스를 통해 ( 설립하고자 하는 법인 이름을 입력해봅니다.


저는 서울중앙지방법원에 등기 할 예정이라 관할등기소를 서울중앙지방법원으로 선택하고 이름을 입력해보니, 아직 ‘체커’라는 이름은 등록되어 있지 않더라고요. 중복되지 않는 상호를 정했다면 1단계는 완료!

2. 본점 임대차계약

법인의 본점으로 사용할 장소가 필요합니다. 알아보니 세 가지 방법이 가능하더군요.

1) 법인 대표이사 자택

  • 업종에 따라서 본인이 자가로 사는 거주지(집)를 본인의 본점으로 사용할 수 있습니다. (저처럼 소프트웨어 관련 업종일 경우에는 본점을 거주지로 할 수 있더라고요)

2) 법인 대표이사 명의로 계약된 임대 건물

  • 법인 등록이 완료되면 임대차계약을 법인 대표자에서 법인으로 다시 갱신하면 됩니다. (즉 건물 주인하고 임대차 계약을 법인 명의로 다시 합니다.)

3) 전전세 또는 부동산무상사용승낙

  • 현재 임대 중인 건물이 법인 대표이사 명의로 계약되지 않은 경우는 재계약이 번거로울 수 있으므로 전전세 또는 부동산 무상사용승낙을 받으면 됩니다. 하지만 두 가지 모두 건물주의 사용동의가 필요합니다.

3. 서류 준비하기

법인 등록에는 ‘주식회사설립등기신청서’, ‘정관’, ‘발기인총회의사록’, ‘기간 단축동의서’, ‘주식발행사항동의서’, ‘주식인수증’, ‘주주명부’, ‘취임승낙서’, ‘인감개인신고서’ 을 비롯해 설립에 참여하는 발기인과 감사의 인감, 주민등록등본, ‘잔고증명서’, ‘등록세영수증’이 필요합니다. (엄청 많죠 -_-; 이래서 법무사에 그냥 맡기나 봅니다..ㅋㅋ)

서류가 이렇게 많다는 사실을 깨닫고, 저는 이 문서들을 가장 빠르고 쉽게 만들 수 있는 사이트를 찾다가 이지비즈라는 사이트에서 법인설립에 필요한 문서를 쉽게 만들었습니다. 수수료로 33,000원을 받긴하는데 법인 만드는 동안 다양한 도움을 받았습니다. 싸게 문서를 제공해주는 대신 TM을 통해서 법인 세무대리인이 필요하니 본인들한테 맡겨달라는 그런 마케팅을 하시더군요. 기존에 알고 계시는 세무사가 없으신 분들은 아마 여러모로 도움이 되실 것 같아요.

4. 서류 만들기

저는 이지비즈에서 다음과 같이 정보를 입력하고, ‘주식회사설립등기신청서’를 비롯해 10여가지의 문서를 자동으로 만들었습니다.


이때 중요한 게 사업목적인데요. 처음에 별생각 없이 ‘뭔 사업 목적을 입력하래~’ 하면서 ‘소프트웨어 개발 및 공급’, ‘소프트웨어 컨설팅’ 이렇게 두 가지 정도만 입력했는데요, 알고 보니 추후 법인 사업자등록증에 업종/업태를 추가하기 위해서는 사업목적에 해당 업종/업태에 관련된 목적이 있어야 하더군요. (라고 이지비즈 TM 담당자가 이야기해줬습니다.) 그래서 저는 위처럼 15개의 사업목적을 추가했습니다.

5. 은행에가서 잔고증명서 받기

이제부터 약간 고된작업의 시작입니다. 은행에가서 자본금이 입금된 통장의 잔고증명서를 발급해야 합니다.

만약 자본금이 1000만원이고, 주주로 참여한 사람이 2명이며 각각 500만원의 자본금을 내기로 했다면, 대표이사의 빈 통장에 1000만원을 입금한 다음 은행에가서 ‘OOO 계좌에 잔고증명서 발급해주세요’ 하면 발급해줍니다.

이때 중요한 건 잔고증명서에 기재되는 날짜가 있는데요 (이지비즈 사이트에 입력했던 날짜), 이 날짜를 기준으로 정관을 비롯해 법인등기에 필요한 수많은 서류의 기준 날짜가 되므로 이 기준 날짜에 해당하는 날에 은행에 가셔야 합니다!

4. 법인 등록세 납부

원래는 구청에 가서 납부를 해야 하는데, 인터넷으로도 납부가 가능해서 저는 5분만에 인터넷으로 냈습니다. 구청에서 내시려면 ‘주식회사설립등기신청서’을 들고 구청 세무과에 가시면 됩니다.

5. 법인도장 만들기 및 발기인과 감사 인감도장 요청하기

이제 수많은 서류에 도장을 찍어야 할 시간입니다. 저는 근처 도장집에 가서 6만원주고 굵고, 이쁘고, 고급진 법인도장을 하나 만들었습니다. 그리고 발기인과 감사로 참여한 분들께 인감도장과 인감증명서, 주민등록등본 1통씩을 요청했습니다.

여기서 도장을 찍으려고 수많은 문서를 쳐다보고 있으면.. 도장을 찍으라고 하는곳이 너무 많아서 머리가 복잡합니다. (걱정하지 마세요. 서울중앙지방법원 등기국에 가니 도장 어디에 찍으라고 다 알려주는 법원 직원이 계십니다. 호호호) 그래서 저는 뭘 찍어야 할지 헷갈리는 부분은 그대로 두고 모든 인감도장(대표이사 및 발기인, 감사)을 챙겨서 등기국으로 갔습니다.

6. 서울중앙지방법원 등기국 방문하기

저는 차 가지고 갔다가, 주차하는데 거의 30분 넘게 걸렸습니다. 주차장이 매우 협소해서 가급적이면 대중교통으로 가세요…ㅜ.ㅜ 번호표 뽑고 담당자한테 가서, ‘제가 혼자 법인을 만들다 보니 도장을 어디에다가 찍어야 할지 몰라서 그냥 가져왔는데, 어디에 찍어야 하는지 알려주실 수 있을까요?’ 라고 이야기했더니 ‘왜 법무사한테 안 하고 혼자 하냐’는 표정으로 쳐다보시며, ‘종합민원실’에 ‘법인설립’이라고 써진 곳에 가서 서류 검토를 하고 오라고 하시더군요. 매우 전문적으로 생기신 분이 제가 가져간 서류들을 보며 도장을 찍어야 할 곳을 정확하게 알려주시면서 서류들을 스테이플러로 하나씩 찝어주시더라고요.(정말 친절합니다) 몇 분간 검토를 해보시더니, 혼자 한 것치고는 빠진 서류도 없고, 서류 하나하나 모두 완벽해 보인다며 칭찬하시며 이제 신청하고 집에 가랍니다.

참, 서류를 신청하기 전에 등기신청수수료 (30,000원)을 내야 하는데 현금을 챙겨가시면 1분 만에 무인처리기에서 수수료를 낼 수 있습니다! 현금이 없으면, 은행에 가서 15~20분씩 기다리며 납부해야하니 현금을 꼭 챙겨가시길…

저는 이 방법으로 첫날 서류준비, 다음날 등기국 방문으로 이틀 만에 법인 등기를 신청 했습니다.

등기가 완료되면, 다시 등기국에 가서 법인인감카드 등록, 세무서에서 사업자 등록, 은행에서 법인통장개설 등을 개설해야 한다고 하니 등기가 완료된 후에 해야 할 일들을 또 써보도록 하겠습니다.

혼자 법인 만드시는 분들께 도움이 되시길~~ 🙂

[출처 :


nwjs를 이용하여 작동되는 웹사이트를 데스크탑 어플리케이션처럼 만드는 방법에 관해서 소개를 해봅니다. 우리가 이 글에서 논하고자하는 것은 웹기술을 이용하여 독립된 어플을 만드는 일반적인 SPA형 어플리케이션이 아니라, 그냥 웹서비스로 접속해도 되는 것을 단순하게 어플리케이션으로 랩핑만하여 제공하는 것에 대한 이야기입니다.

'어차피 브라우저를 통해서 접속하면 되는 것을 굳이 어플리케이션처럼 만들어서 뭐가 좋은가?'라고 할 수 있습니다. 사실 이미 만들어진 것을 랩핑하는 것이라서 환경을 셋팅하는 것 이외에는 그다지 힘이 들지 않습니다. 하지만 단순하게 랩핑하는 것만으로도 아래와 같은 많은 장점들이 있습니다.

1. 독립된 마켓 이용에 따른 마케팅 효과 증대

웹 서비스를 런칭한 이후에는 우리는 많은 광고비를 뿌려 구글과 페이스북에서 사용자들을 유입시키려 노력합니다. 그럼에도 불구하고 웹서비스는 영원히 접근할 수 없는 앱 마켓라는 벽이 존재합니다. 아직까지 윈도우스토어는 활성화되어 있지 않지만 OSX의 경우에는 앱마켓의 접근성이 좋아서 많은 분들이 사용합니다. nwjs를 통해서 랩핑을 하는 경우에 이와 같은 마켓에 등록하여 마케팅 채널을 하나 확보하는 셈이 될 수 있습니다. 와 공짜 마케팅이다

2. 웹키트 기반의 렌더링 사용률 증대

익스플로러 최신버전은 HTML5나 CSS3 스펙을 무리없이 사용가능합니다. 그러나 아직까지 한국에서는 많은 사용자들이 익스플로러 엣지 이전버전을 사용하기 때문에, 웹서비스를 지속적으로 제공함에 있어서 의식할 수 밖에 없는 문제들이 있습니다. 죽어라 익스플로러!! 조삼모사같이 느껴질 수도 있지만, 이러한 고객분들에게 nwjs로 랩핑된 데스크탑 어플리케이션 설치를 유도하면 자연스럽게 지속적으로 웹키트 엔진으로의 사용을 유도하게 됩니다.

3. 작업 표시줄에서 독립된 접근성 확보 가능

독립된 프로그램처럼 작동한다는 것은 곧 작업표시줄에서 브라우저안에 탭으로 존재하는 다른 서비스와 다르게 직접적인 접근성을 확보할 수 있다는 이야기가 됩니다. 창이 따로 논다는 것은 사용자의 활용도에 따라 여러가지로 이점을 제공합니다. 가상 화면을 통해서 하거나 단축키를 통해서 사용되어질 수도 있고, 특정 창크기로 고정하여 사용하게될 수도 있습니다.

4. 네이티브 API의 활용

브라우저상에서 동작하도록 설계된 원래의 웹사이트 목적상, 아무래도 랩핑한 프로그램에서 네이티브 API를 많이 활용하지는 않을 것입니다. 그래도 조건부라도 마음만 먹으면 활용할 수 있으니 일단 장점에 포함된다 생각합니다. nwjs가 활용할 수 있는 네이티브의 목록은 여기에 있습니다.

5. 제작 및 유지보수의 비용 최소화

랩핑을 하는 최초의 과정 이외에 추가적으로 드는 비용이 거의 제로에 가깝습니다. nwjs 전용으로 모든 정적 리소스들을 사용하는 경우라면 조금 다를지 모르겠습니다. 그런데 실제로 돌아가는 사이트를 랩핑하는 것은 모든 자원을 원격에서 끌어와서 동작시키는 것이라, 사실상 이 프로그램은 우리의 웹사이트 하나만을 띄우는 전용 브라우저가 되는 셈이 됩니다.


이전에는 Node-Webkit이라고 불리었으며, 요새 유행하는 Electron(aka atom-shell)과 비슷하게 Chromium과 Node.js 기반에 데스크탑 어플리케이션에서 웹을 구동할 수 있는 좋은 도구입니다. 그래서 데스크탑에서 실행하는 어플리케이션에다가 기존 웹 개발 방법대로 설계 코딩할 수 있습니다.


  • HTML5, CSS3, Javascript과 같은 웹기술 사용가능
  • Node.js API 와 서드파티 모듈 지원
  • DOM 과 Web-Workers를 Node.js에서 호출 가능
  • Javascript 소스 보호
  • MacOS, WindowOS, LinuxOS 모두 호환

NWJS vs Electron

NWJS vs Electron 에서 둘을 비교한 표를 볼수 있습니다. 간단히 살펴보면,

  • 브라우저 런타임용에 차이가 있습니다. NWJS는 Chromium을 바로 사용하지만, Electorn은 Chromium을 커스텀을 통해서 빌드 복잡도를 낮추기 위해 libchromiumcontent을 사용합니다.
  • Entry Point가 다릅니다. NWJS는 HTML,Javascript 둘다 사용되고, Electron은 Javascript만을 제공합니다.
  • Chrome App과 API를 지원 하는 차이 유무입니다.
  • 가장 중요한 이슈 해결시간과 이슈 발생 빈도입니다.. NWJS가 좀 불리합니다.
  • 하지만 Github WatchStartFork는 별차이가 없습니다.

최초에는Electron을 이용하여 제작을 시도하였으나, Electron의 webview를 썼을 때에 기본 자바스크립트의 confirmalertpromptbeforeunload 등이 정상적으로 작동되지 않는 문제점이 있었습니다. 즉, 웹브라우저라면 당연히 되어야하는 스펙들이 Electron Webview에서는 알게모르게 제한이 되는 경우가 있습니다. 분명 안되는것이 더 많겠지만 이쯤에서 깔끔하게 포기하고 nwjs로 돌아서서 더 이상은 모르겠습니다. Electron이 대세인 느낌은 있지만 이렇게 랩핑을 하는 목적으로는 적합하지 않았습니다.


1. 설치

2. 디렉토리 구조

항상 개발 처음 시작할 때 디렉토리 구조를 어떻게 잡을까 부터 고민을 하여 보일러플레이트를 참고하거나 자신만의 구조를 잡기 마련입니다. 저희는 처음 해보는 부분이라 보편적인 디렉토리 구조를 따랐습니다.

NWJS는 아래 예시와 같이 production 배포 부분과 빌드하기 위한 부분을 나뉘어 구조를 잡는게 편합니다. 왜냐하면 빌드에 필요한 node modules이나, resources나 그외 빌드 관련 소스들은 production에 포함되지 않아도 상관없기 때문입니다. 

디렉토리 구조
< 디렉토리 구조 >

주요 부분

  • .cache : 빌드에 필요한 리소스 캐싱
  • build : 빌드 결과물
  • resources : 어플리케이션 icon 관련 리소스
  • src : production 용 디렉토리
    • index.html : entry point

이외 특이한점은 package.json가 빌드 디렉토리와 production 디렉토리에 각각 한개씩 존재합니다. 이는 node module 설치를 다르게 하고, NWJS Config 설정, 빌드 스크립특 작성등 각 용도에 맞게 분기 시키기 위함입니다.

3. 요구사항

  • 기존 서비스중인 tyle.io를 iframe으로 호출
  • 상단에 Native Menu Bar를 설정
  • Window Event(loaded)와 Native Window Event Handler(close) 등록
  • Mac(x64)용 dmg, Window(x64,32)용 MSI installer 생성(Linux 제외)
  • 어플리케이션 종료 후 열때 종료할때 window size 로 resizing

4. 개발

  1. 기존 서비스중인 tyle.io를 iframe으로 호출

    // etnry point HTML에 iframe 쓰던 방식 적용
    <iframe src="" id="tyle" ></iframe>
  2. 상단에 Native Menu Bar를 설정

    Menu Reference

    var MenuItems = {
    "darwin" : { //mac
        "history" : [   // menu label
            label : '뒤로', click : function(){ window.history.back(); }, key: '[', modifiers: 'cmd'
            label : '앞으로', click : function(){ window.history.forward(); }, key: ']', modifiers: 'cmd'
            label : '새로고침', click : function(){ document.getElementById('tyle').contentDocument.location.reload(true); }, key: 'R', modifiers: 'cmd'
    "win" : { //window
        "history" : [   // menu label
            label : '뒤로', click : function(){ window.history.back(); }, key: '[', modifiers: 'ctrl'
            label : '앞으로', click : function(){ window.history.forward(); }, key: ']', modifiers: 'ctrl'
            label : '새로고침', click : function(){ document.getElementById('tyle').contentDocument.location.reload(true); }, key: 'R', modifiers: 'ctrl'
    // Menu 생성
    var addMenu = function(){
    var topMenu = new nw.Menu({type: "menubar"});
    // mac이랑 winodw,linux는 다른 menu 프로세스를 제공하기 위해 또 Menubar 객체를 할당
    var menuBar = topMenu;
    //darwin===mac 이고, 따로 menubar를 생성 후에 시작.
    if(process.platform === 'darwin'){
      menuBar = = topMenu;
    // menuItems' 에서 메뉴들을 가져와 등록.
    for (var menuKey in MenuItems) {
      if (MenuItems.hasOwnProperty(menuKey)) {
        var submenu = new nw.Menu();
          submenu.append(new nw.MenuItem(menuItem));
        menuBar.append(new nw.MenuItem({
          label : menuKey,
          submenu : submenu
    // window,linux 는 따로 menu를 window에 등록.
    if(process.platform !== 'darwin'){ = menuBar;
  3. Window Event(loaded)와 Native Window Event Handler(close) 등록

    window ifrmae이 호출 된 이후 loaded 이벤트가 호출 됩니다. (dom ready랑 비슷 합니다.) 
    Native Window 를 닫으면 close 이벤트가 호출 됩니다.

    var nw = require('nw.gui');
    // 이벤트 정의
    var BrowserEvent = {
        evtList : ['close','loaded'],
        close : function(){},
        loaded : function(){}
    // 필요한 이벤트들 NW.GUI를 통해 등록
    var addEvent = function(){
        nw.Window.get().on(evt, BrowserEvent[evt]);
  4. Mac(x64)용 dmg, Window(x64,32)용 MSI installer 생성(Linux 제외)

    Grunt-nw-builder를 참고하고, nw-builder 레퍼런스와 비슷합니다.

    grunt-nw-builder 는 grunt 기반으로 만들어진 nw-builder 모듈로써 nwjs를 빌드 패키징 작업을 통해서 저희가 실제로 데스크탑 어플리케이션 APP을 생성시켜주며, 인스톨러도 만들어줍니다. 또한 msi-pacakger는 빌드된 결과물을 window installer로 만들어줍니다.

    module.exports = function(grunt) {
    nwjs: {
      options: {
            name : 'tyle',
            platforms: ['osx64','win64'],
            macIcns : './src/resources/osx/tyle_logo_electron.icns',
            winIco : './src/resources/windows/tyle_logo_electron.ico',
            cacheDir : './.cache',
            buildDir: './build',
            flavor: 'normal',
            zip : true,
            zipOptions : {
              forceLocalTime : true,
              comment : " desktop app window version"
            macPlist: {
                CFBundleDevelopmentRegion: "ko",
                CFBundleDisplayName: "tyle",
                CFBundleName: "tyle",
      src: './src/**/*' // Your NW.js app
    appdmg: {
      options: {
        basepath: './',
        title: 'tyle-mac',
        icon: 'src/resources/osx/tyle_logo_electron.icns',
        background: 'resources/osx/dmg-background.png',
        contents: [
          {x: 370, y: 160, type: 'link', path: '/Applications'},
          {x: 140 , y: 160, type: 'file', path: 'build/tyle/osx64/'}
      target: {
        dest: 'build/tyle/osx64/tyle-mac.dmg'
    grunt.registerTask('default', ['nwjs','appdmg']);
    // window msi installer 
    var createMsi = require("msi-pacakger");
    var options = {
     // required
     source: '~/tyle-nw/build/tyle/win64',
     output: '~/tyle-nw/build/tyle/msi',
     name: 'tyle',
     upgradeCode: 'YOUR-GUID-HERE',
     version: '1.0.0',
     manufacturer: '',
     iconPath: '~/tyle-nw/src/resources/windows/tyle_logo_electron.ico',
     executable: 'tyle.exe',
     // optional
       arch: 'x64',
       localInstall: true
     createMsi(options, function (err) {
       if (err) throw err
       console.log('Outputed to ' + options.output)
  5. 어플리케이션 종료 후 열때 종료할때 window size 로 resizing

    window를 종료할 경우 close 이벤트에서 현재 종료창의 사이즈를 로컬 저장소에 json file 로 저장합니다. 그리고 다시 앱을 열었을 때 기존에 저장해놓은 사이즈를 가져와 window를 리사이징을 진행 하는 방식입니다.

    tyle.windowSize = (function(){
    var jetpack = require('fs-jetpack');
    var win = require('nw.gui').Window.get();
    var pjson = require('./package.json');
    // window size를 저장할 공간(directory) 설정
    var userDataDir = jetpack.cwd(nw.App.dataPath);
    var stateStoreFile = 'window-state-tyle.json';
    // 기본 window size (package.json 에서 가져온다)
    var defaultSize = {
        width : pjson.window.width,
        height: pjson.window.height
    // 상태 저장 객체
    var state = {};
        저장된 window size 반환
    var restore = function(){
    var restoredState = {};
        restoredState =, 'json');
    return Object.assign({}, defaultSize, restoredState);
    } /* 현재 window size 구하기 */ var getCurrentPosition = function(){ return { width : win.width, height : win.height } } /* 현재 window size 저장 */ var saveState = function(){ Object.assign(state, getCurrentPosition()); userDataDir.write(stateStoreFile, state, {atomic : true}); } /* 생성된 window resizing 하 */ var resize = function(){ var resize = restore();
    win.width = resize.width;
    win.height = resize.height;
    } return { restore : restore, save : saveState, resize : resize } })();


위 요구사항에 맞게 개발을 진행하면 아래와 같이 기본적인 골격을 갖추게 됩니다. 전반적인 소스를 공유하진 않았지만, 큰 틀에서 벗어나지 않은 것이기 때문에 쉽게 적용할 수 있습니다.

실제 예시
< 실제 예시 >

iframe 방식으로 기존에 개발해놓은 웹을 그대로 가져다 쓸수있고 설치방식과 Chromium 기반이기 때문에 IE에서 겪던 문제를 해결 할 수 있는 좋은 기회를 가질 수 있습니다. 
자 다들 시작 해보시죠?


위에 기술하였듯이 랩핑하는 것만으로 많은 효과들이 있습니다. 하지만 보다 이질감이 없는 사용자 경험을 위하여 데스크탑 어플과 같은 빠른 반응속도를 보장하여야합니다. 전통적인 페이지 리다이렉션으로 인해서 버튼 클릭마다 blank 화면이 번쩍거린다면 사용자로 하여금 내가 가짜 앱을 사용하고 있다고 느끼게 하기 때문이지요.

따라서 Single Page Application으로 제작하여 Spinner가 표시되더라도 UI가 사용자 행동에 즉각 반응하는 것이 좋고, 페이지 리다이렉션이라면 pjax등을 통하여 어떻게든 빠른 반응이 나타나도록 하는 것이 좋습니다(참고 :

[출처 :,]

1. Ubuntu Nginx, Php5 세팅해주는 과정에서 오류가 발생

$sudo nano /etc/nginx/sites-available/default




         location ~ \.php$ {

                try_files $uri =404;

                fastcgi_split_path_info ^(.+\.php)(/.+)$;

        #       # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini


        #       # With php5-cgi alone:


        #       # With php5-fpm:

                fastcgi_pass unix:/var/run/php5-fpm.sock;

                fastcgi_index index.php;

                include fastcgi_params;


이렇게 세팅을 해줬더니 502 Bad GateWay 오류가 났다.

로그를 찍어보니

$ nginx -t

nginx: [warn] server name "" has suspicious symbols in /etc/nginx/sites-enabled/default:28

nginx: [emerg] "fastcgi_pass" directive is duplicate in /etc/nginx/sites-enabled/default:58

nginx: configuration file /etc/nginx/nginx.conf test failed

default파일에서 오류가 발생해서 검색해보니

" The location block provided results in error: nginx: [emerg] unknown directive "fast_cgi_split_path_info" in /etc/nginx/sites-enabled/default:56 Need to remove the underscore between fast and cgi: fastcgi_split_path_info ^(.+\.php)(/.+)$; "

Then comment the first case with php5-cgi alone since php5-fpm is already installed.
I came with the same problem. Hope this could help you.




         location ~ \.php$ {

                try_files $uri =404;

                fastcgi_split_path_info ^(.+\.php)(/.+)$;

        #       # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini


        #       # With php5-cgi alone:

        #        fastcgi_pass;

        #       # With php5-fpm:

                fastcgi_pass unix:/var/run/php5-fpm.sock;

                fastcgi_index index.php;

                include fastcgi_params;


php5-fpm와 cgi 가 중복된다는 뜻인것같아 주석처리후 재시작 했더니 해결

php 파일 업로드 사이즈는 기본적인  php.ini 수정하여 되었다

( 참고)


추가적으로 해줘야 될 사항들.

(용량을 크게 늘린다던지 등..)

[ /etc/php5/fpm/php.ini ]

1. file_uploads = On
2. upload_max_filesize 10M
3. post_max_size 10M
4. max_input_time 300
5. max_execution_time 300
6. memory_limit 128M

* post_max_size > upload_max_filesize 
[ /etc/nginx/sites-available/default ]
1. client_max_body_size 100M



// check if fields passed are empty

if(empty($_POST['name'])   ||

   empty($_POST['email']) ||

   empty($_POST['message']) ||



echo "No arguments Provided!";

return false;


$name = $_POST['name'];

$email_address = $_POST['email'];

$message = $_POST['message'];

// create email body and send it

$to = ''; // hi mate thanks for purchase guna theme, just replace your email with

$email_subject = "Contact form submitted by:  $name";

$email_body = "You have received a new message. \n\n".

 " Here are the details:\n \nName: $name \n ".

 "Email: $email_address\n Message \n $message";

$headers = "From:\n";

$headers .= "Reply-To: $email_address";


return true;


했는데 메일이 가지않는문제발생

 sudo apt-get install sendmail

Indeed, the steps I took were 1) If sendmail isn't installed, install it: apt-get install sendmail 2) Configure hosts file correctly: nano /etc/hosts And make sure the line looks like this: localhost localhost.localdomain yourhostnamehere 3) Run the sendmail config and answer 'Y' to everything: sendmailconfig I restarted apache for good measure: service apache2 restart After that my mail was sending instantly.


sudo sendmailconfig


         rewrite ^/pricing.Aloha$ /pricing.html;

        rewrite ^/en/pricing.Aloha$ /en/pricing.html;

        rewrite ^/$ /index.html;

        rewrite ^/en/$ /en/index.html;

        rewrite ^/about.Aloha$ /about.html;

        rewrite ^/en/about.Aloha$ /en/about.html;

        rewrite ^/contact.Aloha$ /contact.html;

        rewrite ^/en/contact.Aloha$ /en/contact.html;

        rewrite ^/facility.Aloha$ /facility.html;

        rewrite ^/en/facility.Aloha$ /en/facility.html;

        rewrite ^/faq.Aloha$ /faq.html;

        rewrite ^/en/faq.Aloha$ /en/faq.html;

/etc/nginx/sites-available/default 파일 내



안에 수정해주면됨 

+ Recent posts