...+155+124
=571,096


엊그제에 밤2시까지 삽질해 성공한 기념으로 아주 간략하게 작성. 질문 받습니다.
여기서 말하는 "닷홈"이란 한국식 LAMP 스택의 shared hosting이라고 보시면 됨.


사전 준비사항

  • 이 글은 모든 작업을 윈도우에서 한다고 가정한다. 리눅스/맥 쓰시는 분들은 훨씬 쉽게 할 수 있는 작업이니 필요한 명령줄은 알아서 연구하시길.
  • 최소한의 php 상식
  • php, composer (라라벨 인스톨러는 쓰지 않는다.)
  • Git 관리툴 (FTP로 수동배포하는 과정에서 버전을 관리하기 위한 용도. 잘 모르면 소스트리 쓰자.)
  • FTP 클라이언트 (닷홈에 수동배포하려는 용도. 잘 모르면 파일질라 쓰자.)
  • PHP 5.6.4 이상이 돌아가는 닷홈 계정 (닷홈에서 PHP 7.0을 굴리려면 무제한 호스팅을 받아야 한다. 여기서 도메인 구매하는 댓가로 FREE 티어를 쓸수있게 해놓았으니 참고하시길... 제길 이런글 쓴다고 레퍼럴 받는것도 아닌데)


tl;dr

이하의 서술은 닷홈 호스팅의 두 가지 문제를 극복하는 과정을 안내한다. composer와 artisan을 못 쓰는 문제, 요청 실행 담당 파일이 마음대로 지목되지 않는 문제. 이 두 가지가 별문제가 아니라고 생각된다면 더 읽지 않아도 됨.

로컬에서는 php artisan make:migration create_foo_table 같은 콘솔도 쓸 수 있고 composer install foo 같은 것도 얼마든지 실행할 수 있지만 닷홈의 대부분 호스팅 상품은 공식적으로 SSH 접속이나 컴포저 실행, 쉘스크립트 실행 등을 할 수 없다. 요즘처럼 모든 것이 패키징 체계로 되어 있는 웹앱 개발 환경에서 이는 치명적인 제약이다.

게다가 로컬의 라라벨과 프로덕션인 닷홈은 웹 요청 수행 방식이 다르다. 라라벨은 루트폴더의 server.php를 실행하는데, 닷홈은 별도의 .htaccess 설정이 없는 한은 걍 아무 생각 없이 각 계정에 대하여 /host/home숫자/계정명/html 디렉토리의 index.htm(l)이나 index.php만을 열고 보기 때문이다. 어떤 요청에 대해서 .htaccess가 특정 파일을 포인팅해 주지 않으면, 그 요청의 응답은 영원히 403404, 500 또는 310(Too many redirects)뿐일 것이다.

여기에 사소한 몇 가지 요점을 짚고 가려고 한다. 예컨대 라라벨 버전 문제인데, PHP 7.1이 아닌 PHP 7.0의 서버라면 라라벨 버전은 5.5까지만 쓸 수 있다. 이제부터 조금씩 살펴보자.


1. 상쾌하게 새 앱으로 시작하기 (단, 버전에 맞춰서)

이미 로컬에서 돌아가고 있는 라라벨 앱이 있다면 최악의 경우 그 앱을 다운그레이드해야 하는 상황이 올 수 있다. 그런데 라라벨에서 다운그레이드란 새 프로젝트를 낮은 버전으로 시작해 거기에 마이그레이션하는 것만을 뜻한다. 그렇게 따지면 아마도 이 스텝은 필수인 듯.

1-1. 라라벨을 올리고 싶은 닷홈 호스팅의 PHP 버전을 알아내고, 이 버전을 지원하는 최신 라라벨 버전이 뭔지 각 버전별 설치 문서에 가서 확인해 본다. 대리클릭을 해드리자면 PHP가 7.0일 땐 5.5까지, PHP가 5.6.4 이상일 땐 5.4까지.

1-2. 앱을 설치할 디렉토리로 가서 커맨드를 열고 라라벨 설치 명령을 실행한다. 예컨대 설치할 수 있는 라라벨 최고 버전이 5.5버전대라면 이렇게 입력한다. (자동으로 현시점 최신 버전인 5.5.28을 깔아준다.)

> composer create-project laravel/laravel 앱이름 "5.5.*" --prefer-dist

1-3. 컴포저 특유의 무반응이 잠시 이어지다가 한바탕 폭풍 설치가 끝나면 Git 관리툴을 열고 이 폴더를 기존 존재하는 저장소로서 생성해 커밋&푸시하고 버전 관리를 시작한다.
소스트리 기준으로 설명하자면: New Tab > Create > 지금 만든 폴더 선택 > 저장소 이름이나 나머지는 뭐 알아서 > 생성 > Yes > 작업공간 > 모두 스테이지에 올리기 > 커밋 > Push.

1-4. 설치된 디렉토리로 이동해 php artisan serve를 실행해본다. 별문제가 없어야 한다.

1-5. /public 폴더에 assets라는 이름으로 빈 폴더를 하나 만들고 거기에 css 폴더와 js 폴더를 때려넣어 둔다. 3번 스텝에서 해야 할 작업을 미리 해놓는 것.


2. 최초 버전 unzip 배치

이짓을 하는 이유는 /vendor 디렉토리에 너무 많은 폴더와 파일이 들어 있기 때문이다. 이걸 전부 FTP로 올리는 것은 바보짓이며, 번번이 이렇게 할 수도 없는 노릇이다. composer가 바로 이 문제를 해결한 툴이지만, 우리는 닷홈 서버를 빌려 쓰고 있어 컴포저를 쓸 수 없으니 일단 갓 구워진 라라벨 정도로만 올려보자. AWS 엘라스틱 빈스톡에서 영감을 얻음.

2-1. 라라벨이 설치된(=server.php 파일이 있는) 폴더의 내용 전체를 압축해 파일명.zip을 만들어둔다.

2-2. 다음 코드를 적당히 활용한 적당한이름.php 파일을 만들어둔다.

<? header("Content-Type: text/html; charset=UTF-8"); ?>

<a href="?unzip=true">Unzip 실행</a>

<?php
if ($_GET['unzip']) {
$zip = new ZipArchive;
if ($zip->open('파일명.zip') === TRUE) {
$zip->extractTo('./');
$zip->close();
echo '<br><br>배치 성공';
} else {
echo '<br><br>배치 실패';
}
} else {
phpinfo();
} ?>

대단히 스트레잇포워드한 코드인데 설명하자면 이렇다. 파일을 웹브라우저로 딱 보면 Unzip 실행이라 써진 링크 하나가 있고 그 밑에 이 서버의 php 정보가 줄줄이 달린다. 링크를 누르면 그 정보가 없어지고 배치가 성공했는지 실패했는지를 알려준다.

2-3. FTP 클라이언트로 닷홈 계정에 접속해 파일명.zip 압축파일과 적당한이름.php 파일을 같이 루트폴더에 올린다. (닷홈은 전형적으로 /html 폴더가 루트임)

2-4. http://닷홈계정주소/적당한이름.php 에 접속해본다. 설명한 대로 링크 하나와 php 설치정보가 떠야함.

2-5. 과감하게 Unzip 실행을 한다.

2-6. 배치 성공이라 떴는지, 그리고 실제로 FTP로 새로고침을 했을 때 로컬에 보이는 폴더와 파일들이 막 보이는지 확인한다.

2-7. 큰 산을 넘었다는 안도감을 가지도록 한다. (이후 필요할 경우 /vendor 폴더에 대해서만 이 꽁수를 써서 일괄 업로드할 수도 있을 것이다. extractTo() 함수는 덮어쓰기가 기본이므로 별문제 없을 것.)


3. public 폴더 바꿔치기

이 스텝은 왜 필요한가 하면 앞서 설명한 index.php를 루트에 띄워주기 위해 필요하다. 희한하게도 서양에서는 'public' 폴더가 닷홈 호스팅의 'html'에 해당한다고 한다. 요컨대 라라벨 앱 업로드 과정에서 그 public을 이 public으로 자연스럽게 덮어쓸 수 있으면 OK라는 것이다. 그래서 물정 모르는 서양 답변자들은 가끔 "퍼블릭폴더 위에 앱을 설치하면 그만인데 왜 우는소리를 해~" 같은 속터지는 소리를 한다. 하지만 닷홈은 그게 안 되니까 이짓을 하는 것. 결정적으로 도움이 된 것은 이 문서.

여기서는 라라벨을 닷홈 서버의 루트에 설치한다고 가정한다. 이 작업 자체는 닷홈 서버에서만 수행한다.

3-0. 잘 모르겠다면 다음 내용을 복사해 루트폴더에 .htaccess 파일로 저장하고 접속해 본다. 해결이 안 되면 다음 스텝으로 넘어간다. Thanks to 잘보고갑니다

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>

3-1. 1-5를 아직 해놓지 않았다면 지금이라도 해놓고 닷홈서버에서도 동일하게 적용해 준다.

3-2. /public 폴더에 있는 '파일들'을 모두 복사해 그 위 폴더인 루트 폴더에 붙인다.

3-3. 라라벨 5.5 기준으로는 지금 붙여넣은 루트폴더의 index.php에 이런 라인들이 있을 것인데, 각각 알맞게 고친다.

require __DIR__.'/../vendor/autoload.php'; // 이렇게 생긴 라인은
require __DIR__.'/vendor/autoload.php'; // 이렇게 고칠것

$app = require_once __DIR__.'/../bootstrap/app.php'; // 이렇게 생긴 라인은
$app = require_once __DIR__.'/bootstrap/app.php'; // 이렇게 고칠것

한마디로, /public 폴더 기준으로 써져 있었던 로딩 파일들 주소를 루트폴더 기준으로 고친다.

3-4. 하나 더, 루트폴더에 있는 server.php를 고쳐준다.

# 고치기 전

if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
    return false;
}

require_once __DIR__.'/public/index.php';


# 고친 후

if ($uri !== '/' && file_exists(__DIR__.$uri)) {
    return false;
}

require_once __DIR__.'/index.php';

3-5. http://닷홈계정주소/home 에 접속해 본다. 뷰 파일을 건드린 적이 없는데도 css와 js 파일이 로딩되지 않아 와장창 깨지고 있을 것이다. 이 부분을 4단계에서 해결한다.


4. 환경 구분해주기

이제 거의 모두 해결됐고, 3단계에서 해결되지 않은 퍼블릭 애셋 경로 문제만 남는다. php 소스들 자체는 로컬에서나 닷홈에서나 같아야 하는데, 로컬에서는 asset() 함수에 'assets/css/app.css' 경로를 넘겨줘야 되는 반면 닷홈에서는 'public/assets/css/app.css'를 넘겨줘야 된다. 어쩌면 좋냐고? htaccess 쓸줄 모른다고? 걱정마시라. 이런 건 env로 아주 간단하게 조치하면 된다.

4-1. 로컬의 루트에 있는 .env 파일을 편집기로 열고 다음 설정을 아무데나 추가한다.

ASSETS_DIR=assets

4-2. 소스코드 내 필요한 모든 곳에서 다음과 같은 찾아바꾸기를 실시한다. 4-1에서 저장한 변수를 불러오도록 하는 것.

// 바꾸기 전
{{ asset('assets/css/app.css') }}

// 바꾼 후
{{ asset(env('ASSETS_DIR').'/css/app.css') }}

4-3. 로컬의 .env 파일 내용 전체를 복사한 다음, 닷홈서버의 루트에 .env 파일을 새로 만들고, 그 파일의 내용을 방금 복사한 내용으로 덮어쓴 다음에, 이 부분을 고친다.

ASSETS_DIR=public/assets

4-4. 이것으로 우리는 마침내 로컬에서 돌아가는 여러분의 앱이 닷홈에서 그 모습 그대로 똑같이 돌아가는 것을 보게 되었다. 앞으로 애셋을 불러와야 할 일이 있을 때는 아묻따 env('ASSETS_DIR') 하나로 오케이.


5. 부록: 버전을 관리해서 필요한 파일만 추가 FTP 배치하기

아주 간단히 설명하고 지나간다.

5-1. Git 클라이언트를 열어 마지막으로 푸쉬해 놓은 로컬 커밋 지점이 어디인지, 정말 그 지점이 현재 닷홈 서버상으로도 최신이 맞는지 확인한다.

5-2. 로컬에서 아무 생각 없이 하고 싶은 작업을 완료한다.

5-3. Git 클라이언트 작업 공간에서 지금 어느어느 파일이 바뀐 건지 확인한다. composer install로 패키지를 깔았다면 git에는 잡히지 않으니 주의.

5-4. Git을 통해 파악한 변동 있는 파일들만 골라서 수동으로 FTP로 닷홈에 덮어쓴 다음 실제 접속해 봐서 잘 적용됐는지 확인한다. 이론상 로컬과 완전히 똑같아야 한다.
새로 패키지를 설치한 경우 composer.json 자체는 잡히니까 역시 닷홈에 올려 덮어써 주고, /vendor 디렉토리에서 새로 생긴 디렉토리를 통째로 올린다.
잘 모르겠으면 2번스텝에서 만들어 놓은 코드를 활용해 /vendor 디렉토리를 압축한 파일을 올리고 그파일을 /vendor 디렉토리에 덮어쓰는 코드를 실행시켜도 된다.

5-5. 문제가 없으면 지금 변동 내역을 모두 스테이지에 올리고 커밋&푸시해서 다음에 작업을 시작할 기준점으로 삼는다. 문제가 있으면 과감히 직전 버전으로 롤백한다.


6. 부록: 아티즌 콘솔 써서 마이그레이션 하기

닷홈서버의 DB에 이미 잔존하는 테이블을 활용하는 거라면 걍 DB 덤프받아 로컬에 복붙해 시딩하고 php artisan make:model Foo 돌려 모델 만들고 그걸 배포하면 그만이지만, 없던 테이블을 만든다면 라라벨의 컨벤션에 맞게 정식으로 스키마를 만들어 마이그레이션하는 과정을 거쳐야 한다.

6-1. 일단 로컬에서 마이그레이션 직전까지 해놓는다. php artisan make:migration create_foo_table을 실행해 파일 자동 생성을 시키고, 생성된 파일의 up() 메소드와 down() 메소드를 적당히 매뉴얼 봐 가면서 채운다.

6-2. /routes/web.php 파일의 맨 끝에 아티즌 콘솔 명령을 실행하는 라인을 하나 넣는다.

Route::get('/artisan_console', function(){
Artisan::call('migrate');
});

6-3. 로컬에서 localhost:8000/artisan_console 에 접속해 up() 메소드가 계획대로 잘 돌아갔는지 확인하고, 확인되었으면 닷홈서버의 /database/migrations 디렉토리에 생성된 마이그레이션 파일을 올린 다음 /routes/web.php에 로컬과 같은 수정내역을 적용해, 마찬가지로 접속 실행한다. 닷홈DB에 migrations 테이블이 자동으로 만들어지게 되는데, 없으면... 걍 하나 만들면 됨.


후기

  1. 쓸데없이 길어졌네요. 제가 읽고 싶었던 문서를 제가 쓰다 보니 그렇게 되었던듯
  2. 아무튼 .htaccess는 웬만하면 건드리는 것이 아닙니다. 혹시 RewriteBase, RewriteCond, RewriteRule 같은것 붙잡고 씨름하고 계시다면 머리를 비우고 새로 다시 처음부터 생각해 보시길.
  3. 도움이 되려나 모르겠네요.


Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

  1. 잘보고갑니다
    2018.04.17 21:35 신고
    댓글 주소 수정/삭제 댓글
    /html/.htaccess

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule ^(.*)$ public/$1 [L]
    </IfModule>

    얘는 어떨까요?
    • 2018.04.18 11:48 신고
      댓글 주소 수정/삭제
      ㅎㅎ 이렇게간단한 방법도 있군요!!
      PHPSlim에서는 되는것 방금확인했는데 라라벨에서는 어떨지 모르겠네요 (아마 될듯)

이 글은 수시로 업데이트하는 기술 공부 블로그 포스팅입니다. 이런거 공유하면 확실히 낚여줄 내주변 지인이 최소 1명 있긴 한데... 그에게는 이글이 대윾잼+대환장 띠용글로 보일것이지만 알고도 굳이 씁니다. 목표는 1년뒤 나역시 이걸 보며 대윾잼+대환장을 느끼는것


0. 서론

머 아무튼 2017년도 다 저물어가는 마당에 호주 워홀 마치고 돌아와서 취직은 해야겠고 마땅히 벌어놓은 것이라고는 각종 기획의 잔꾀와 워드프레스/코드이그나이터[각주:1]로 좀 굴러본 것뿐인지라… 최소한의 최신 기술과 개념을 익혀놓지 않으면 안 되겠다는 약간의 절박함이 생겨, 귀국 후 크리스마스 이브에 과감하게 세팅하고 시작한 것이 yuptogun.com 프로젝트.

지금까지 걍 구상만 했던 각종 웹앱들을 *.yuptogun.com 형태의 서브도메인 아래에서 각기 구현해서 돌리고, 개중 잘 팔리는 게 생기면 별도 도메인으로 독립시켜서 운영하고, 그러면서 호스팅업체 서버 스택 아래에서 할 수 있는 최대한의 신기술&신개념을 막 도입해 써보는 운동장으로 쓸 계획.[각주:2]


1. 스택: shared LAMP stack hosting + Codeigniter + uncompiled (in-browser) React

A. 서버 스펙. 일단 주어진 서버 환경을 서술하자면... 전형적인 한국 PHP 웹호스팅 스택. 리눅스에 아파치와 MySQL을 깔고 PHP를 상시 켜놓으며 가상 할당 공간에 실행&전송할 파일을 올리는 방식이다. 월 5천 원을 더 내면 SSH 접속을 시켜준다고 하지만 지금은 그마저도 하고 있지 않아서 그야말로 20세기 FTP 홈페이지 스타일. 쉘접속이고 서버설정이고 그딴거ㅇ벗다.

AWS도 있고 다른 옵션도 많은데 굳이 이걸 선택한 이유는 딱 두가지.

  • 비용: 닷홈의 혜자급 상품으로, 도메인값만 닷홈에 내면 디스크 1G와 트래픽 무제한 이용이 무료로 가능하다. 써보다가 트래픽이 증가하면 상위 상품으로 올릴수있음. (물론 하위 상품으로 다운그레이드는 안된다)
  • 시장 친숙성: 서비스 자체를 서비스하는 IT 기업을 제외하면 나의 '클라이언트'가 될 사람들은 보통 shared web hosting 환경에서 뭔가를 하려고 한다.[각주:3] 따라서 임대형 쉐어 웹호스팅 환경이라는 (프로그래머에게는) 꽤 열악한 스택을 선택하기로 함.

B. 프레임워크. PHP라는 서버 언어가 주어진 상황에서 제일 최신 프레임웍을 써볼라치면 아무래도 라라벨이나 심포니 같은 걸 해야겠지? 아니면 좀 힙하게 Slim? 사실 호주에 있을 때 이것저것 건드려 봤는데 결국은 그간 해왔던 코드이그나이터를 골랐다.

  • 가볍다. PSL-7을 준수하는 프레임워크들은 FTP로 배치하는 것이 사실상 불가능하다. 파일이 보통 많아야 말이지. 로컬에서 작업한 걸 통째로 zip으로 묶어 그걸 올리고 unzip을 (쉘로 하건 php코드로 하건) 실행하는 게 그나마 the best practice so far더라.[각주:4]
  • 버전 관리를 할 것이 없다. Ion Auth나 TCPDF 말고는 딱히 더 갖다 쓸 서드파티 라이브러리도 없고, 대부분의 요소들은 어차피 사용자단에서 돌아가야 하는 파일들인지라 그냥 CDN에서 퍼와도 되겠다고 판단했다.
  • Slim은 라우팅까지 성공했고 라라벨은 배치하는 것까지 성공했으나[각주:5] 지금은 백엔드 로직보다는 프론트엔드의 react 공부에 집중하고 싶어서 경험이 있는 Codeigniter를 채택. 보아하니 아직은 코드이그나이터가 php 실무 바닥에서는 현역인 듯도 하고.
  • 뭐 말이 Codeigniter지 가급적 API 생성기로만 사용하고, 각종 라이브러리나 유틸리티는 서버에서 돌아가는 게 효율적일 때(세션관리 등)만 쓸 예정. 애초에 서버란 JSON만 송수신해주면 되는거니까.

C. 뷰처리. 여기서부터 모험이 시작된다. views/template/react.php 파일을 만들고 여기에서 reactjs cdn이라고 검색해서 나오는 파일 두 개와 babel cdn이라고 검색해서 나오는 파일 하나를 불러오게 시켜놨다.

  • 사실 React 생태계가 추구하는 방식대로 하자면 절대 이렇게 해서는 안 된다. 특히 Babel은 내가 짠 text/babel 스크립트 파일을 서버에서 미리 컴파일하는 데 쓰여야 하지, 브라우저에서 낑낑거리면서 실행할 물건이 아니다.
  • 그치만 뭐 일단은 콘솔에 인포 메시지 하나 뜨는 거랑 깜박임 문제[각주:6] 말고는 기능상 별 문제를 모르겠어서 그냥 넘어가고 있는중.


2. 기본 흐름: CI는 json과 뷰파일 목록만 구성하고 나머지는 React가 한다

메인페이지를 만들면서 결정한 설계는 다음과 같음.

  1. 기본 라우팅에 따라 컨트롤러가 실행된다.
  2. 컨트롤러는 다음 세 가지를 주로 실행한다.
    • 로딩될 뷰파일을 결정한다.
    • 뷰파일에서 사용할 데이터를 만든다. SEO용 메타태그, 현재 로그인한 회원 정보 등
    • 뷰파일에서 json으로 출력할 배열을 만든다. 필요한 DB 테이블 쿼리 결과를 json_encode()로 뽑아 $this->data['table'] 형태로 넘긴다.
  3. 메인뷰는 다음 세 가지를 실행한다.
    • 컨트롤러가 넘긴 데이터를 가지고 html 마크업을 뿌린다. 메타태그, 로그인했을 경우와 아닐경우에 따른 html 마크업 등등
    • 컨트롤러에게서 물려받은 json을 스크립트 태그로 html 출력 맨끝에 뿌린다.
      <script>var theData = <?= $data; ?>;</script>
    • 그리고 assets들(React 라이브러리, 메인 js, 각종 CSS 등등)을 불러온다.
  4. 그러면 이제 뷰가 불러온 메인 js 파일은 브라우저 안에서 다음과 같이 작동한다.
    • 메인뷰가 서버 응답으로서 돌려주는 DB 데이터 json을 props/state로 참조한다.
    • 이 props/state와 자기 안에서 const로 규정돼 있는 정적 정보들(e.g. 쿼리결과 없을시 뿌릴 문자열들 등)을 이용해, 엽토군이 정의한 대로 열심히 컴포넌트 클래스들을 렌더링한다.
장황하게 적었지만 코드를 보면 아주 간단한 이야기다. 해당 페이지의 응답결과는 예컨대 위쪽에 <script src="app.js">가 있고, 중간에 <div id="wrapper"></div> 같은 텅 빈 요소가 하나 있으며 그 밑으로 조금 내려가서 보면 아주 장황하게 긴 json을 data 변수에 할당한 script가 한두 개 있는 식이다. 그리고 app.js는 data.table.forEach((record) => {어쩌고저쩌고}) 형태의 라인을 가진다.
어떤 assets을 불러올 것인지를 뷰에서 결정할 것인가 컨트롤러에서 결정할 것인가는 지금은 좀 가닥 안잡혀서 일단 죄다 뷰에서 결정중. 아마 컨트롤러에 다 때려넣는게 더 말은 맞겠지(극단적으로 말해서 뷰파일은 그냥 div#app 하나만 덜렁 갖고있어도 될지도 모른다).


3. 깨달은 것들: React

React를 막무가내로 공식문서 봐가면서 공부하고 적용해 현재까지 깨달은 것들은 다음과 같다.
  • 만약 당신이 MVC 구조에 익숙하고, 새로운 라이브러리의 문법을 배우는 데 큰 문제가 없으며, 지금 당장은 리액트를 가지고 화면에 DB자료를 뿌리는 정도로 충분하다면, 다른 것보다 Thinking in React를 따라가는 것이 가장 좋고 가장 빠르다. 당신이 알아야 할 것은 거의 대부분 나와 있으며, props와 state 개념을 실전으로 바로 알 수 있다. 장황하게 createClass가 어쩌고저쩌고 하는 설명도 생략돼 있어, 필요한 기초에 집중할 수 있다.
  • state는 최상위 컴포넌트에서 통제되어야 한다. 글로 읽을 땐 영 뭔말인지 몰랐는데 실전을 해 보니까 간신히 이해가 되더라.
    예컨대 이런 상황이 있다고 생각해 보자.
#wrapper가 #sidebar와 #content의 2개 요소를 가지고 있고, #sidebar 밑의 a[data-tab=x]를 누르면 #content 밑의 #x가 보여야 함

jQuery라면 구현 자체는 아주 간단하다. .data('tab')으로 x값 얻어와서 $('#content')를 통제할 때 그 값을 넘기면 된다. 하지만 이 구현이 일으키는 변화는 필연적인 것이 전혀 아니며 오히려 아주 우연하고 작위적이다.
이 상황을 React는 이렇게 해결한다.

#wrapper는 #sidebar 밑의 a에 대해서도 책임이 있고, #content 밑의 div들에 대해서도 책임이 있다. 따라서 #wrapper의 this.state.tab을 정의해 주고, #content는 항상 이 state에 대응하는 div를 보여주도록 한 다음, #sidebar 밑의 a가 눌릴 때는 #wrapper의 this.state.tab이 적절히 업데이트되도록 한다. 그러면, #content 밑의 div는 즉시 자동으로 그 state에 반응("react")한다!

이렇게 써놓고 보니 왜 다들 그렇게 열광하는지 알것도 같음. 데이터가 어디로 흐르는지 볼 수 있고 훨씬 더 논리적이다. (단 그만큼 프론트엔드가 초반에 머리 싸매고 시작을 해야 하는 것. 어느 객체가 최상위인가? 정말 모두 한 개의 파일/스크립트 안에 넣어야 할까?)

  • 문자열을 직접 인젝션하는 코딩은 잘 안 된다. 예컨대 html 마크업을 구성하는 문자열은 이스케이프되어 문자그대로 브라우저에 뿌려진다. 정 그걸 띄우고 싶다면 악명높은 dangerouslysetinnerhtml을 쓰든지 차라리 리액트 html 오브젝트로 넣어버리라는 것이 공식 조언이다.
    또 남들은 다 된다고 하는데 나만 안 되는 것이 뭐나면… 문자열을 전달받아 그 문자열과 일치하는 이름의 컴포넌트 렌더링하기. 흔히 알려진 React.createElement(ChildName, null) 용법이 작동을 안 한다.[각주:7]
  • render () 메소드 내에서 const로 만드는 빈 배열은 길이가 0이 아니라 1이다. 이유는 모르겠다. 그래서 forEach push로 필터링을 할 때 결론적으로 이런 식의 이상한 판별식을 쓰고 있다. 이게 아닌데... 분명 더 옳은 방법이 있는데...
if (filteredArray[0].props.name != '') { /* do the stuff */ }
  • PHP가 세션/쿠키를 만드는 과정/방법론 때문에 사용자 인증이 생각보다 번거롭다. Ion Auth를 쓰고 있는데, 최종적으로는 기껏 깔아놓은 react router를 못쓰고 결국 자기 자신에게 로그인폼을 POST 제출해서 로그인 처리하고 리디렉션 시키는 몹시 전통적인 짓을 하고 있다.
    • 원래 계획은 ajax 폼으로 그 자리에서 즉각 로그인을 시키는 것이었다. 로그인 자체는 성공했는데, 문제는 자동로그인("keep logged in") 기능. 자동로그인이 도무지 먹지를 않는다. 왜 그런가 하고 모든 소스를 다 뜯어본 결과… "자동로그인"은 일반적으로 이런 로직으로 동작한다는 것을 알았다.
    1. 사용자가 POST 요청으로 로그인을 시도한다.
    2. PHP는 ID, 해싱된 패스워드, 자동로그인 여부를 받아 로그인(유저정보 매칭)을 시도한다.
    3. 로그인이 성공하면 PHP는 지금 로그인한 세션 고유값을 DB에 저장한다.
    4. 그리고 즉시 HTTP 응답 헤더에 이 세션 고유값이 포함된 쿠키를 쓰라는 요청을 보내는 리디렉션을 실행한다.
    5. 이때 PHP가 별다른 출력을 내지 않아야 비로소 이 쿠키가 써진다.[각주:8]
    6. 리디렉션된 HTTP 응답에서부터 쿠키와 세션은 유효해진다.
    7. 그래서 그 다음부터 (리액트 라우터가 아닌) PHP가 HTTP 요청을 받으면 우선 서버단에서 쿠키와 세션을 확인해 로그인 여부를 체크하고 그제서야 그 결과를 반환해 이용할 수 있게 한다. 여기서 자동로그인 구현 완료.
    • 여기서 5번 스텝이 문제되는 것이었다. ajax 요청은 필연적으로 출력을 낳고, 따라서 자동로그인 로직의 5번에서 멈춘다.[각주:9]
    • 그러면 ajax 요청 콜백에서 적당히 쿠키를 만들면 되는것 아니냐? 싶을 텐데 그러면 7번 스텝이 걸림. 요컨대 자동로그인을 체크하고 로그인해서[각주:10] → 사용자 정보를 가져온 뷰를 → F5로 새로고침하면[각주:11] → 거짓말처럼 로그아웃당함.[각주:12]
    • 서버단이 JS로만 구현되었거나 자동로그인 로직이 좀 달랐다면[각주:13] ajax 콜백의 쿠키 작성만으로도 충분히 자동로그인을 할 수 있었을까 하는 미련이 남지만... 그건 먼 훗날 많은 이치를 깨닫고 나서 다시 보기로. 지금은 우선 로그인을 시켰다는 데 의의를 두면서, u/(.*) 라우팅과 컨트롤러의 u() 통제와 users.js 최적화로 타협한다.[각주:14]

99. 해야될것들

  1. Redux 배워서 써먹기
    • "최상위 컴포넌트에 스테이트 때려박는게 너무 귀찮아서 온갖 방법으로 그걸 회피하는데요 그래서 state management 라이브러리를 씁니다...!"
    • 아직은 귀찮거나 헤비하지 않지만 넘나 당연하게 react의 짝패로 쓰이는 모양이니 싫어도 적용해야할듯.
  2. webpack 배워서 써먹(으면서 필요한 의존성 버전관리하)기
    • "근데 리액트 사용의 정석은 php 파일에서 로드하는거는 아니에요 webpack으로 빌드해서 index.html 및 js / assets 뭉치를 만들고 걔를 static hosting"[각주:15]
    • 지금 당장은 이런 스텝들이 필요할듯
      • React, Babel을 npm으로 받아서 로컬 트리에 넣어놓기
      • 지금 있는 메인 js를 번들 가능하게 조정하기
      • 컨트롤러 $dev 변수에 따라서 지금식으로 다 로드하느냐 bundle-*.js만 불러오느냐를 스위칭할 수 있게. (이건 순 내 편의를 위한 편법. 충격적이게도 지금은 메인 js 고칠때마다 서버에 업로드하고 새로고침을 해서 테스트를 본다고 한다...)
    • 뭐 일단 해놓으면 앞으로는 npm update 한번씩만 돌리면 되겠지
      • 잘은 몰라도 나중되면 지금 뷰파일이 로딩중인 fontAwesome, pureCSS 등등도 죄다 이걸로 관리하게될듯


  1. 그리고 대략 다섯 달 동안이지만 충격과 공포의 해피CGI 솔루션(...)을 경험해봤다. PHP 5.2 이하에서 실행해 달라고 요구하는 솔루션을 들어보신 적이 있는지? 한국에서는 무려 이새끼가 아직도 현역이다. 팩트TV가 이회사 물건으로 돌아가고 있다. [본문으로]
  2. 어차피 만들려는 웹앱들의 본질이 TODO를 크게 벗어나지 않기 때문에 가능한 짓이다. [본문으로]
  3. 보통은 애초에 "서버언어" 개념 자체를 안 갖고 계시니. [본문으로]
  4. 예전의 다른 웹호스팅에 라라벨 올리면서 찾은 방법. 다시 하기는 싫다. [본문으로]
  5. 일단 로컬 구조 그대로 올린다 → 루트의 index.php가 public/index.php 를 require하도록 고친다 → public/index.php 가 ../bootstrap/autoload.php 와 ../bootstrap/app.php 를 require하도록 한다. 이렇게 하면 FTP만 되는 호스팅 환경에서도 일단 라라벨을 굴릴 수는 있음. [본문으로]
  6. 인브라우저 컴파일 중일 때는 아무것도 안 뜨다가 잠시 후에야 가상DOM이 그려지는 문제. 리액트 사용상의 대표적인 대환장 포인트 중 하나라고. [본문으로]
  7. 선배왈 스위치문 써서 컴포넌트 리턴하는 거 말고는 답이 없다고. 안그래도 나도지금 그렇게 하는중이다. 글쎄 이게 아닌데... [본문으로]
  8. 내가 이해한 바로는, 쿠키 생성 코드가 포함된 코드 라인들의 최종 목적이 '뭔가를 출력하는 것'이어서는 안 된다는 느낌이다. 예컨대 redirect()는 괜찮지만 return $resultjson은 안 된다. [본문으로]
  9. 별짓을 다해 확인해 본 결과, 세션도 만들어지고 쿠키 요청도 들어가지만 실제 쿠키 생성부터가 안된다는 것으로 판명. CORS도 확인하고 별짓을 다해봤는데 그게 문제가 아니라 결국 setcookie() 함수의 결함이랄까 설계 때문에 안된다. [본문으로]
  10. 여기선 클라이언트(브라우저)가 단지 쿠키만을 작성한다. [본문으로]
  11. 여기선 서버가 존재하지 않는 로그인정보 세션을 체크하려고 시도한다. [본문으로]
  12. 매치해 볼 세션이 없으므로 자명한 결과. [본문으로]
  13. 대충 찾아보니 JWT(JSON 웹토큰)과 로컬스토리지 사용이 JS 환경에서는 가장 통념으로 사용되는 듯하다. [본문으로]
  14. 그리고 (서버와 리액트가 라우팅을 놓고 싸우는 탓에) 꽤 속도가 느려졌으며 여전히 두세 시간 뒤면 세션이 풀려버린다. 왜죠... [본문으로]
  15. 느닷없이 그런 말을 들어도 지금은 잘모르겠는 부분이지만 여튼 이바닥 선배가 그렇다고 하니 그런줄 알자. [본문으로]
Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

주어진 임무:

10팀의 아티스트가 만든 10개의 전시물에 대해 국문/영문 소개문, 사진, 아티스트 소개 등을 보여주는 그럴듯한 모바일 브로셔를 만들어라.


실제 돌아가는 결과물: upcyclingtree.dothome.co.kr


전체 소스:

github.com/yuptogun/Upcycling-Tree-Mobile-Flyer




Day 1

  • 백엔드가 의외로 고민거리였다. 아무 생각없이 정적 페이지 10*n개를 만들면 머리는 편하겠지만 손이 고생할 거다. 10개의 팀과 10개의 작업물과 각 작업물에 들어가는 n개의 사진을 그렇게 매뉴얼하게 관리하다 보면 분명 어딘가 하나쯤 꼬일 테니까.
  • 요컨대 데이터에 최소한의 체계성을 부여해야 한다. 그렇다고 DB 스키마 짜고 CI 같은 본격적인 프레임워크를 깔고 싶지도 않다. 너무 일이 된다. phpSlim 같은 간단한 라이브러리는 아직 건드려도 못 봤고... 음 어떡한다?
  • 일단 MVC 자체는 매뉴얼하게 가기로 결정하고 array.php를 작성. 내가 알아보고 관리할 수 있는 선에서 가장 원초적인 정적 데이터배열을 만든다. 이걸 include한 다음 get파라미터에 담긴 변수 따라서 원소 출력시키지 뭐. 어차피 모바일로 잠깐 보고 끌 페이지이기 때문에 URL prettify는 안 해도 된다고 판단.
  • 프론트엔드가 그 다음 골칫거리였다. 아무 생각없이 Bootstrap이나 PureCSS를 쓰면 손은 편하겠지만 눈이 고생할 거다. 애초에 모바일 브로셔로서 갖춰야 할 최소한의 미적 감각을 담보할 수가 없을 것이다. 뭐 괜찮은 프론트엔드 프레임워크 없나? 막 뒤져봄.
  • 예전에 한번 보고 지나갔던 Framework 7이라는 놈을 다시 찾아냄. 안드로이드/iOS 앱개발자들이 웹개발도 비슷한 느낌으로 해낼 수 있도록 만든 (것으로 생각되는) 소스이다. 지원하는 컴포넌트를 살펴보니 이건뭐 거의 아이폰 네이티브 앱 와꾸가 나오는 게 구상 및 설계상의 문제가 일거에 해결됨. CDN이 있나? 살펴보니 있었다! 오케이 너로 정했다.
  • index.php를 짜면서 CSS 클래스와 컴포넌트, js에 익숙해지는 데 하루의 나머지를 다 보냄. 아직은 전체 틀만 잡고 있었으므로 근본 원리는 파악하지 못한 (몰라도 괜찮았던) 단계. 이때는 $data[i][9](index에서 띄울 섬네일 url) 같은 것이 없었다.
  • 메뉴 구현은 다음과 같다. navbar사이드패널 여는 버튼을 넣고, 그 안에 사이드패널 닫는 버튼을 하나 만들어 넣기. 별문제없이 작동.
  • 이미지 호스팅은 비공개 텀블러를 사용하기로 함. 되는 거 확인하고 퇴근.



Day 2

  • 출근하자마자 원래 index.php에 통째로 들어 있던 header.php 부분과 footer.php 부분을 분리. 잘 작동하는 것 확인. (개인적으로 뷰를 아예 새로 작성할 때 !doctype html로 시작해서 전체를 다 짜둔 다음 header, footer 등으로 오려내는 습관이 있다. 좋은 습관이 아니다.)
  • 최소한의 그리드가 필요하다는 것을 알게 되어 pureCSS의 base.css만 따로 로딩함.
  • 사이드패널에 뿌려놓은 메뉴 링크들을 눌렀는데, 404나 에러페이지로 넘어가지 않고 그냥 깜박이고 마는 것이 너무 신기했다. 이개모지? 나중에 쓰겠지만, 프레임워크7의 핵심 작동 원리인 "AJAX 프리로딩" 덕분에 가능한 것이었음.
  • framework 7의 기본 CSS는 사이드패널에 리스트로 띄운 메뉴 텍스트를 죄다 자동으로 ellipsis로 줄여버리더라. 작품들의 이름이 좀 길고, 모바일에서 그걸 다 볼 필요가 있기 때문에, 걍 CSS를 뜯어고쳐 다 띄우게 만들어둠.
  • 이제 핵심이라고 할 수 있는 detail.php를 작성하기 시작. 원래 구상은 이랬다.
    - 적당한 GET파라미터가 지정이 안돼있으면 홈 주소로 리디렉션시킨다. 최소한의 url 에러처리.
    - tree 파라미터가 지정돼 있을 경우 "작품소개" 탭을 활성화시켜 작품 소개를 바로 띄운다.
    - team 파라미터가 지정돼 있을 경우 "작가소개" 탭을 활성화시켜 작가 소개를 바로 띄운다.
    - 작품소개 탭 페이지에서는 "한국어"를 누르면 국문 설명이, "English"를 누르면 영문 설명이 나오게 한다.
    - 사진 쪽을 누르면 갤러리를 띄운다. 갤러리에 들어갈 사진들은 array.php에서 하위배열 하나 넣어서 foreach로 뿌리기로.
    뭐 더 설명할 것 없이 무쟈게 기초적인(≒무식한) 네비게이션이다.
  • 작품소개-사진-작가소개는 탭바를 이용하기로 했고, 한국어/English 전환은 을 쓰기로 했다. (이름은 비슷한데 영 달라서 되게 헷갈림.) 사진 갤러리는 포토브라우저로 처리.
  • 갤러리에 넣을 사진은 곧 받기로 했었으므로 일단 lorempixel을 채워넣음. "Close"를 "닫기"로 고칠 순 없을까? 생각했지만 일단 넘어감. 작가 소개문은 따로 제공받지 않았었으므로 일일이 웹사이트나 SNS를 찾아가서 소개문 및 로고를 찾아 array.php를 보강함. 페이지별로 멀쩡하게 뜨는 것 확인.
  • 쫙쫙 잘 뜨길래 야 신난다! 하고 이것저것 눌러보다가… 버그 발견. 사이드메뉴로 detail 뷰에 진입했다가 다른 detail 뷰를 보려고 하면 탭바가 동작을 하지 않는다. 그냥 흰 화면이 뜨고 아무 일도 일어나지 않음. 읭??? 이거 뭐냐??? 모바일 사용자는 눈에 보이는 모든 것을 다 눌러보는 법인지라, 이건 무시할 수 없는 문제상황이라는 판단이 서서 패닉에 걸림.
  • 세 시간 동안 스택오버플로를 ㅈ나게 뒤지고 Framework 7 공식 문서를 눈 빠지게 읽음. "해결법"은 못찾음. 설마 싶은 것들은 한두 개 눈에 띄었지만, 이리저리 코드를 고치고 뒤집다 보니 이젠 뭐가 뭔지 모르겠다 싶어서 그만두기로 하고 퇴근. (진짜 뇌도 스택으로 돌아간다면 이런 게 오버플로일까 싶은 걸 경험했다. undo redo를 아무리 눌러봐도 기억이 돌아오질 않았다…)




Day 3

  • 출근해서 서브라임 텍스트를 딱 켰는데 생각해 보니 해결된 게 아무것도 없었다. 최악의 경우 이 조치를 단행하면 되겠지… 라고 짐작하고 있던 조치를 단행했다. 모든 링크에 .external 클래스를 부여한 것. 해놓고서 다시 열어보니, 프레임워크7 특유의 스무스한 페이지 움직임(써보면 안다. 진짜 앱 사용하는 것처럼 움직인다)은 없어졌는데 내용 출력 자체는 멀쩡하게 잘 되었다. 허탈.
  • 차차 원인을 이해할 수 있었다. 프레임워크7은 기본적으로 모바일앱이 갖는 페이지 개념 아래 개발돼 있어서, 하나의 "페이지(URL 엔드포인트)"가 경우에 따라서 여러 데이터를 가진다는 개념을 이해하지 못함. 그런데 나는 index 아래의 모든 뷰가 detail.php?team=foo 아니면 detail.php?tree=bar 꼴이었던 것이다.
  • JS 입장에서는 할 말을 잃고 동작을 중단할 수밖에 없다. 아까 detail 페이지 안에 들어 있는 #tab_tree 하나를 active 걸었는데, (사용자 입장에서는 별개의) detail 페이지 안에 있는 #tab_tree에 active를 또 걸라니 뭔 소리냐 싶겠지.
  • 이런 일이 있을까봐, 그리고 Framework 7의 기술적 특성상 준비돼 있던 것이 a태그에 붙일 수있는 .external 클래스였다. 프레임워크7은 페이지를 쫙 읽어본 다음 href 붙은 모든 url을 미리 ajax로 읽어놓는다. 그 다음 스마트하게 동작한다. 예컨대 A라는 링크를 요청했을 때 서버가 404를 외쳤다면, JS는 A를 기억해 놨다가, 사용자가 A를 누를 때 아무 일도 안 일어나게 만든다. 이게 그야말로 모든 링크에 대해 작동할 경우 외부 사이트 DOM까지 읽게 되면 보통 큰일이 아니게 되므로, 구분자를 제공하는 것.
  • 이 구분자의 기능은 DOM7 편입을 안 시키는 것. 그러면 ajax 프리로딩 없이 그냥 매번 요청된 URL을 새롭게 요청하게 된다. 당연히 "문제"는 해결된다. 하지만 더 좋은 사용(스무스한 이동 등 진정한 framework 7 사용경험 구현)으로서의 진정한 해결은 안된 상태. (미숙…)
  • 그리고 어제의 패닉 리서치 과정에서 한 가지 더 알게 된 사실은, 탭바는 DOM7 전체에서 항상 나오는 메뉴로서 쓰는 것이 best practice라는 것. 생각해 보면 메인화면에 안 나오던 탭바가 세부사항 페이지에서는 나오는 일은 거의 없다. 이것도 아마 스크립트 충돌에 일조한 사항일 것.
  • 전날 뒤집어엎으면서 엉망진창 만들어놓은 코드를 재정리. 공식 문서에 따르면 Framework 7의 전형적인 페이지는 다음 구조를 따른다.
    body
    - panel (상단 전체메뉴. 싫으면 넣지마슈~)
    - views (상단을 뺀 나머지 화면 전체 wrapper. 전체DOM에서 유일)
    -- view .view-main (기본화면, 전체DOM에서 유일)
    --- navbar
    --- pages
    ---- page (data-page=home)
    ----- page-content
    ------ content-block
    --- toolbar
    ---- tabbar 등등
    -- view .foobar (만약 이 웹페이지가 아이패드 메모앱처럼 split된 화면을 가져야 할 경우 추가해서 사용함)
  • 그렇게 허탈하게 버그를 때려잡고 나니 힘이 빠져서, index.php의 대표이미지들 위에 그라데이션을 뿌린다든가 하는 세부적인 스타일링을 트윅하며 기운을 되찾음.
  • 2일째에 정신없이 문서 읽고 깃헙 레포 뒤지다가 문득 발견한 것이 있어 "음 그럼 해볼까?" 하고 footer.php에 넣어둔 포토뷰어 JS 변수를 건드려봄. 'Close'를 '닫기'로 고치는 게 생각보다 너무 간단하게 되었다. photos 배열 넣는 배열 자리에 원하는 변수명의 값을 새로 지정해준 것만으로 적용이 됨. (이걸 간단한 일로 만들어준 개발자가 대단한 거다…)
  • 최종 점검 마치고, 아이패드/아이폰 뷰 체크한 다음 최종본으로 발행.




교훈

  • Framework 7은 앱 개발을 하는 기분으로 모바일웹을 개발하는 도구이다. 단순 컴포넌트 CSS를 묶어서 이쁘게 만들어주는 정도가 아니라, 각 개별 페이지들이 웹앱으로 작동하도록 AJAX로 처리한다.
  • 따라서 Framework 7을 쓰려면 get 파라미터 대신 URL 라우팅과 data-page에 신경을 쓸 것이며, 막무가내로 아무데나 컴포넌트를 끌어다 쓰면 안 된다(요소들 위치 정해져 있는 정도가 꽤 까다롭다). 가급적 문서를 처음부터 천천히 읽고 제대로 개념을 이해한 다음 쓰기. 나처럼 실전 case위주로 막덤비면 공부도 안되고 효율도 떨어진다.
  • 모바일 브로셔는 무료호스팅으로도 유지 가능한 트래픽을 자랑한다. (…) 여러분 올해가 가기 전에 코엑스 B홀 앞에 가 보세요!


Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

스킨 이름은 아직 안 정해졌습니다. 일단 개인용입니다. 색은 뭐 그냥 랜덤하게 뽑다 보니 미묘한 금색이 뽑혔습니다.



LIVE DEMO 보기




기본 화면일단 기본적으로 이렇게 생겼습니다.


일반 메뉴팝업메뉴 버튼을 누르면 메뉴가 뽑혀 나옵니다.


코멘트 입력란코멘트나 트랙백 넣는 곳은 이렇게 생겼습니다. 크 누가 만들었는지 겁나 이쁘다.


아이폰 가로모드 기본화면아이폰 가로모드 등의 환경도 완벽하게 지원(하려고 )합니다.


아이폰 가로모드 메뉴팝업메뉴 버튼을 누르면 상단 고정 메뉴가 쭈르륵 내려옵니다.




사용된 외부 리소스:

  • pureCSS (grids, buttons, forms)
  • jQuery + jQuery UI
  • FontAwesome


남은 작업:

  • 트랙백/코멘트 수 표시, 각종 로그페이지 구현(을 할 것인지 어떤지 결정하기)
  • 공유 버튼 추가
  • 기본 메뉴가 지나치게 길 경우에 대한 가로/세로 레이아웃 대책마련 (overflow: scroll 이란게 구체적으로 어떻게 먹히는건지 알아보고 구현할 필요)
  • footer에 이것저것 몰아넣기
  • 실제 태터툴즈 구조에 맞도록 id, class 조정 ← 서로 엉키지 말아야할텐데…
  • 최종적으로는 마크쿼리 스킨과의 차별성 확보. 사실 좀더 어그레시브하게 짜고 싶은 요소들이 많았는데 그렇게 하면 indication 내지 direction이 안될거같아 그냥 좀더 노멀하게 가고 있다. 아작스 로딩화면이니 첫화면 슬라이드니 하는 거창한 것들은, 이게 내 블로그를 위한 작업이지 남을 위한 것은 아직 아니므로, 일단은 구현하지 않을 생각.


이 블로그 스킨도 사실 꿰매고 기운 곳이 많아서... 이제 곧 싹 갈아엎은 이쁜 블로그 스킨으로 찾아뵐 수 있을 것 같습니다. 기대하시라 ㅋㅋ

Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

쓸만한 한글웹폰트가 왤케 없나 (그래서 예전엔 내가 아예 하나 만들었지만... 이젠 웹환경이 달라져서 그딴걸로는 택도 없다. -_-;) 싶어서 둘러보며 소스 뜯어보다가 혹시나 이게 img 태그로 먹히나 해서 보니 먹힘. 그래서 올려봅니다.



1. 폰트클럽 (산돌)

ReturnFontTextImage.asp

fontclub.co.kr의 폰트샵에서 사용하는 폰트 미리보기 서비스 코드. 적절한 파라미터 값을 주면 이미지를 반환한다.


(현재까지 알려진) 파라미터

FontName
사용될 폰트 이름. EUC-KR 인코딩. 유효한 값은 폰트샵 각개 폰트 미리보기 이미지 URL을 복사해서 해당 파라미터 부분 보고 찾으면 됨.


FontImageText
출력할 문자열. EUC-KR 인코딩.


FontSize
출력될 문자열의 크기. 자연수. 기본값은 24. 픽셀 단위는 아닌 듯.
출력되는 문자열 위에 빈 공간이 붙기 때문에 이 변수가 ImageHeight 변수보다 20% 정도 더 작지 않을 경우 글자 아랫단이 잘린다.


ImageWidth, ImageHeight
각각 문자열을 출력할 이미지의 가로 및 세로 크기. 이 캔버스 안에 들어오지 않는 문자열은 잘린다.

예제

http://shop.fontclub.co.kr/include/font/ReturnFontTextImage.asp?FontImageText=%C0%DF%B5%C7%B4%C2%C1%F6%BA%B8%B0%DA%BD%C0%B4%CF%B4%D9&FontSize=50&ImageWidth=720&ImageHeight=65&FontName=210%20%C4%DE%C7%BB%C5%B8%BC%BC%C5%B9%20B



2. font.co.kr (윤디자인)

preview.aspx

font.co.kr의 폰트샵에서 사용하는 폰트 미리보기 서비스 코드. 적절한 파라미터 값을 주면 이미지를 반환한다.


(현재까지 알려진) 파라미터

fontname
사용될 폰트 이름. 일반 URL 인코딩. 유효한 값은 폰트샵 각개 폰트 미리보기 이미지 URL을 복사해서 해당 파라미터 부분 보고 찾으면 됨.


text
출력할 문자열. 일반 URL 인코딩.


fontsize

출력될 문자열의 크기. 자연수. 기본값 없음, 0 이상의 자연수를 무조건 넣어야 함.
어떤 이유인지 모르겠지만 height 값의 0.85배(추측)보다 fontsize 값이 클 경우 출력 자체가 되지 않는다.


width, height
각각 문자열을 출력할 이미지의 가로 및
 세로 크기. 이 캔버스 안에 들어오지 않는 문자열은 잘린다.

예제

http://generatorutf8.font.co.kr/preview2/yoonfont/preview.aspx?text=%EC%9E%98%EB%90%98%EB%8A%94%EC%A7%80%EB%B3%B4%EA%B2%A0%EC%8A%B5%EB%8B%88%EB%8B%A4&fontname=Yoon%EC%9C%A4%EA%B3%A0%EB%94%95%20710&fontsize=40&width=350&height=47



뭐 고수분들은 이 정도 소스만 가지고도 이리저리 잘 활용하실 수 있을 것 같긴 한데...

코드는 윤이 좀더 좋고 선택 범위는 산돌이 훨씬 넓네요. 어떻게 하면 잘 훔쳐쓸 수 있을까

Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

  1. bleu1115
    2016.05.26 10:20 신고
    댓글 주소 수정/삭제 댓글
    첫번째 산돌 이미지는 깨져서 나오는데
    이제 저 방식으로 사용할 수 없는 걸까요?
    • 2016.05.27 10:03 신고
      댓글 주소 수정/삭제
      아 폰트샵 주소가 바뀌었네요. 수정넣었습니다.
      사실 산돌폰트는 sandollcloud.com 서비스를 가입해서 쓰는게 비용대비 제일 효율이 높습니다.
  2. bleu1115
    2016.05.27 16:05 신고
    댓글 주소 수정/삭제 댓글
    오.. 잘 되는군요! 감사합니다. ㅎㅎ

모든 아이디어가 퍼뜨릴 만한 가치가 있는 것은 아니다. Not every idea is worth spreading.


"아프니까 청춘이다", "당신이 사는 곳이 당신이 누구인지 말해 줍니다" 등 누가 들어도 잘못된 말과 사고방식과 아이디어에 우리가 일상적으로 노출되어 있다는 사실은, 이 글을 읽고 있는 누구나 알고 있다.
그러나 생태주의는 어떠한가? '서바이벌' 쇼는 어떠한가? 유비쿼터스 세계는 어떠한가? 지금의 세계화는 무조건 옳은가? 기발하고 참신하다는 이유로 극악한 신종 범죄의 수법을 굳이 뉴스로 선전해야만 하는가? 왜 모든 기술(Technology)과 교육(Education)과 디자인(Design)은 그 존재 자체로 비판의 대상이 되지 않는가? 진흙으로 벽돌을 굽는다는 '기술', 모두의 언어를 하나로 모으는 '교육', 하늘 꼭대기에 닿게 탑을 쌓는다는 '디자인'은 결국 무엇을 건축하였던가? 인류에겐 바벨탑이 한 번으로는 부족하였는가?

우리는 의심하고 비판하고 재고하고자 한다.
⊥ED 컨퍼런스는 바로 이 일을 할 것이다. 오늘날 TED 컨퍼런스로 대표되는 "아이디어는 무조건 확산해야만 하는 것"이라는 하나의 교조(dogma)에 저항할 것이다. 수많은 아이디어에 사회적, 역사적, 윤리적 책임을 부여할 것이다. 모두 다 옳고 좋아 보이던 아이디어들 사이에서 모순(⊥)을 점검할 것이다.

청춘이기 위해 아파야 하며, 내가 누구인지 말하기 위해 내가 어디에 사는지를 따져야 하며, 어디서부터 무엇이 잘못되었는지조차 알지 못한 채 화려하고 세련돼 보이는 최신 유행을 따른 죄로 '하여간 뭔가 분명히 크게 잘못된' 세상을 살고 싶지 않다면,
⊥ED conference에 참여하라.

Eojin Kim
Founder
yuptogun@gmail.com


⊥ED Conference가 기본적으로 추진할 일들입니다.

1. ⊥ED 컨퍼런스
뭔가 미심쩍고 잘못된 것 같은데 아무도 문제를 제기하지 않는 각종 현상과 슬로건과 '상식'에 의문을 제기하는 5~10분 분량의 강연을 듣고, 연사와 청중이 거침없이 문답을 주고받는 자리입니다. ⊥ED 컨퍼런스의 핵심 행사지요. TED 컨퍼런스가 연사와 청중을 까다로운 기준으로 선정해서 초대한다면, ⊥ED 컨퍼런스는 여러분을 초대합니다! 청중이 되어 가장 날카로운 질문을 던지거나, 연사가 되어 가장 강력한 의문을 제기하세요!

2. Video Podcasting
⊥ED 컨퍼런스의 실황을 촬영하여 YouTube, Vimeo 그리고 팟캐스트로 내보낼 것입니다. 10분을 넘지 않는 시간 동안 한 번도 의문시해 보지 못했던 것들을 의문시하게 하는 무료 비디오 프로그램입니다! 이게 진짜로 세상을 바꾸는 아이디어의 시작 아니겠습니까? 아닌 것을 아니라고 말하는 비디오가 얼마나 빠르고 강력하게 얼마나 많은 사람에게 영향을 끼치는지, 우리는 직접 목격하게 될 것입니다.

여건과 조직을 갖추면 이런 일들도 추진하고자 합니다.

3. 간행본 출판
서점 베스트셀러 진열대에 가 보십시오. 모든 책이 너나할 것 없이 자기가 유일한 정답이며 해결책이라고 아웅다웅하고 있습니다. 사실, ⊥ED Conference는 그 현상 자체에도 의문을 제기할 수 있을 것입니다! 그렇다면 <⊥ED Conference Opus '13> 같은 책이야말로 언제나 꾸준히 탐독될 만한 충분한 자격이 있는 책이 되지 않을까요? 강연 내용을 전체 수록하고, 연사와 각계 전문가 여러분의 감수를 거친 좀더 심도 있는 논의 등을 충분히 수록하여 꾸준히 발행한다면, 인류에 공헌하는 바가 있을 것입니다.

4. 대안을 실현하기(Kick-starting)

"A는 잘못됐다, A는 B가 되어야 마땅하다"라는 강연을 듣고 크게 감동하셨다고요? 그리고 정말로 A를 B로 만드는 것도 생각해 보니 그다지 어렵지 않을 것 같다고요? 아마 대부분의 의문 제기가―TED처럼 거창한 최첨단의 기술과 디자인과 상관없기 때문에―실현하기 쉬울 것입니다! ⊥ED 컨퍼런스는 의문을 주고받으며 비판하는 것을 넘어서서, 굳이 최첨단과 최대 규모일 필요가 없는 수많은 대안들을 제안할 것이고, ⊥ED Conference의 이름으로 그와 관련된 사업 착수를 지원할 것입니다.


⊥ED Conference의 연사가 되기 위한 다섯 단계입니다.

1. "아무도 문제제기를 하지 않는 어떤 것"에 대한 불만과 의문을 떠올려 보세요. 잘못된 사회 통념, 불쾌하게 유행하는 말과 행동, 나 빼고 모두가 당연하게 여기고 있는데 도무지 왜 당연한지 알 수 없는 것들을 생각해 보시면 됩니다. 이것이 보통 강연의 서론이 됩니다.

2. '나는 왜 그것이 불만일까?' 또는 '그것의 정체와 실상은 이러이러한 게 아닐까?'에 대한 답을 만들어 주세요. 달리 표현하자면 그 불만의 근원 또는 그 의심스러운 것의 정체를 폭로하는 것입니다. 논리적·학문적으로 완벽하지 않아도 좋습니다. 당신이 느끼는 그대로를 최대한 끌어내십시오. 이것이 보통 강연의 본론이 됩니다.

3. "그것을 대체해야 할 최선의 방안"을 반드시 제시해 주세요. 허황되거나 이상주의적이어도 괜찮습니다. 청중을 한번 크게 웃기는 '개드립'도 아주 좋습니다. 하여간 대안은 반드시 필요합니다. 이것이 보통 강연의 결론이 되기 때문입니다.

4. 이 세 가지를 최소 3분, 최대 10분 이내에 전달할 수 있도록 정리해 주세요. 저희는 TED와 같은 유창한 화술과 스펙터클한 프리젠테이션에는 관심이 없습니다. ⊥ED 컨퍼런스의 최대 관심사는, 당신의 불만과 의문이 얼마나 날카롭고 솔직하고 전면적인가입니다.

5. 여기까지 다 준비하셨다면 yuptogun@Gmail.com으로 강연 취지와 원고가 담긴 전자우편을 전송해 주십시오. ⊥ED의 면밀한 검토와 협의 후에, 당신은 머지않아 컨퍼런스에서 가장 중요한 연사로서 단상에 초청될 것입니다.

아닌 것을 아니라고 말하세요. ⊥ED Conference의 연사가 되세요. 얼마나 많은 사람들이 당신의 고발을 기다리고 있는지 모릅니다.


"⊥ED"를 읽는 방법은 자유입니다. 가장 형식적인 발음은 'falsumED'지만 크게 상관은 없습니다. 뒤집힌테드, 업택드, 모순드, prepED, 엿테드, 마음대로 부르셔도 좋습니다. 참고로 페이지 관리자는 폴숨드라는 발음 연습에 실패해서 결국 '역테드'라고 부르고 있습니다.

다만 문자표기는 정확하게 해 주시기 바랍니다! ⊥(폴숨)은 수학 기호(ㄷ+특수문자) 중에 있습니다. ㅗ로 ⊥을 대체하는 것은 허용하겠습니다.


너무 즉흥적으로 벌린 바람에 수습하지 못하고 일단 접은 기획. 더 깊게 생각할 시간이 필요합니다. 그렇게 생각을 해 보았는데도 아무래도 안 되겠다 싶으면 완전히 짬시킬 수도 있습니다.


'1 내 > ㅎ 열외' 카테고리의 다른 글

새 블로그 스킨 작업중!  (0) 2015.03.05
fontclub, font.co.kr 미리보기 렌더러 스펙  (4) 2014.01.31
⊥ED Conference  (0) 2013.05.19
김어진정자10.ttf v2.9 업데이트  (7) 2013.04.19
dok.do 단축URL 생성 북마클릿 깔기  (0) 2013.03.30
공익광고  (0) 2011.09.27
Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

도대체 이게 얼마만이야!!!



안녕하세요, 2003년에 김어진정자10이라는 웹폰트를 내놓았던 엽토군입니다. (그땐 중3이었지...)

정확히 10년만의 업데이트를 들고 돌아왔습니다.



(jeongja10.ttf가 해당 파일입니다.

보이지 않으시면 여기로→ https://www.box.com/s/3ro1hwuxy8yvwegk4e1e)


v2.9 업데이트 내역

글립(자소) 7개가 추가되었습니다. 눝, 늍, 똠, 쎼, 쎾, 쑛, 찦

일부 자소의 kerning 및 정밀화를 실시했습니다.


배포는 오직 이곳에서만 이루어져야 합니다. 위의 box.net 링크 또는 이 홈페이지(yuptogun.tistory.com)을 중개하는 방식 외의 "다운받아 다른 곳에 재첨부하기", "직접 다른 사람에게 복사해주기" 등등을 일체 엄금합니다. 그 외의 거의 대부분의 사용은 자유입니다. 누가 만들었는지 확실하게 하고 싶어서 저작권자가 요구하는 최소한의 제약조건이오니 부디 지켜 주십시오.


v3.0 업데이트 예고

완성형에서 조합형으로의 확장 이전을 단행합니다. (11172자를 전부 쓰실 수 있게 됩니다.)




잡썰


'1 내 > ㅎ 열외' 카테고리의 다른 글

fontclub, font.co.kr 미리보기 렌더러 스펙  (4) 2014.01.31
⊥ED Conference  (0) 2013.05.19
김어진정자10.ttf v2.9 업데이트  (7) 2013.04.19
dok.do 단축URL 생성 북마클릿 깔기  (0) 2013.03.30
공익광고  (0) 2011.09.27
랜덤게임 아이디어 "나는 뭔데?!"  (0) 2010.05.01
Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

  1. 헐...
    2013.05.30 14:27 신고
    댓글 주소 수정/삭제 댓글
    대박이네요.ㅎㅎㅎ
    얼마전에 저작권 폰트 정리 좀 하다가
    출처를 알 수 없거나, 혹은 출처가 사라졌거나 했던 폰트들이 있어서
    이걸 상업적으로 사용해야하나 마나 고민하고 있었는데
    오늘 우연히 혹시 싶어 엽토체를 검색해보니 정~~말 오랜만에 업데이트가 나왔군요.ㅎㅎㅎ
    설마 아직까지 출처가 남아있을 줄은 몰랐네요.
    왠지 반가운 마음이 드는....ㅋㅋ
    • 2013.05.31 00:53 신고
      댓글 주소 수정/삭제
      ㅋㅋㅋㅋㅋ... 저도 이젠 이 폰트의 저작권을 제가 주장할 수 있다는 게 신기하답니다...
      방학 중에 두 서체를 완전 조합형으로 전환시킬 계획입니다. 기억해 두셨다가 가끔 체크해 주세요! (사실 가장 해야 할 작업은 자연스러운 곡선으로 바꾸는 일이지만... ㅋㅋ)
  2. 2015.02.14 12:34
    댓글 주소 수정/삭제 댓글
    비밀댓글입니다
    • 2015.02.16 00:40 신고
      댓글 주소 수정/삭제
      감사합니다. 이런 사연들이 있어 뭐라도 열심히 해보자는 다짐이 새로 섭니다.
  3. 2016.03.19 03:30
    댓글 주소 수정/삭제 댓글
    비밀댓글입니다
    • 2016.03.20 09:07
      댓글 주소 수정/삭제
      비밀댓글입니다
    • 2016.03.21 11:42 신고
      댓글 주소 수정/삭제
      네 그렇게 쓰시라고 만든 폰트입니다. 자유롭게 사용하세요. (폰트 파일 자체로 수익을 내지만 않으면 됩니다.)

※ 현재 이 서비스를 지원하는 메조미디어가 서비스를 일시중단한 상태입니다.


dok.do라는 URL shortener(긴 주소 짧게 만드는 페이지)가 있습니다. (스팸문자 보내는 사람들이 악용하는가 보더군요.)

http://dok.do



웹으로 들어가서 줄이고 싶은 원래의 긴 주소를 입력하면 바꿔 주는데, 대부분의 URL 단축기들이 지원하는 북마클릿(bookmarklet)이 없길래 실망스러워하다가, 조사해 보니 만드는 게 어렵지 않다는 것 같아서 시도해 봤습니다. 난 자바도 소스 살짝 수정하는 것밖에 모르는데...


그리고 5분만에 해냈습니다!!!

-.-;;




Dok.do 즐겨찾기로 사용하기



1. 다음 자바스크립트 전체를 복사합니다. java 부터 text') 까지입니다.


javascript:void(location.href='http://dok.do/api/shorten?longUrl='+location.href+'&format=text')



2. 사용하고자 하는 브라우저에서 아무 페이지나 일단 즐겨찾기(북마크)합니다.


예를 들면 이렇게 1



3. 즐겨찾기 수정으로 들어가서 방금 즐겨찾기한 페이지의 URL(주소)를 방금 복사한 자바스크립트로 바꿔치기합니다. 페이지 타이틀도 원래의 제목에서 'dok.do로 줄이기' 같은 걸로 바꾸면 좋겠죠.


예를 들면 이렇게 2



4. 이제 앞으로 주소를 줄이고 싶은 페이지에서 수정한 그 즐겨찾기를 누르면 이렇게 작동이 됩니다.


예를 들면 이렇게 3



원래는 goo.gl을 가장 많이 썼는데, 이 방법을 직접 찾아낸 이상에는 저도 dok.do를 좀더 많이 써보려고 합니다. 작동 원활하게 잘 됩니다.

이 단축URL 서비스가 오래 가면 좋겠네요. dok.do의 about에 들어가 본 외국인들이 독도가 한국령인걸 좀 알길 바라며...

'1 내 > ㅎ 열외' 카테고리의 다른 글

⊥ED Conference  (0) 2013.05.19
김어진정자10.ttf v2.9 업데이트  (7) 2013.04.19
dok.do 단축URL 생성 북마클릿 깔기  (0) 2013.03.30
공익광고  (0) 2011.09.27
랜덤게임 아이디어 "나는 뭔데?!"  (0) 2010.05.01
근래 해본 사고들  (2) 2010.01.03
Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

공익광고

2011.09.27 22:53
좀 도와주십쇼
 


자세한 정보 및 공식연락처는↓
바로그텀블버그 모금일기(RSS) / 바로그트위터 / 바로그텀블러(블로그) 아니면 저한테 직접
Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절

0. 이 글은 랜덤게임 '나는 뭔데?!'를 설명하기 위해 쓰는 글이다. 저녁점호 중에 '사랑해 병신'을 하다가 문득 생각해봤다.
1. (일반모드) 술래부터 오른쪽으로 돌아가면서 왼쪽 사람을 찍으면서 숫자를 1부터 하나씩 부른다. 그게 그 사람 번호이다.
(하드모드) 술래부터 오른쪽으로 돌아가면서 아무나 찍으면서 숫자를 1부터 하나씩 부른다. 찍힌 사람이 그 번호를 받는다. 단 같은 사람이 두 번 찍히면, 그는 두 번째로 자기를 찍은 사람에게 달려가 멱살을 잡고 "나는 뭔데?!"를 외치면 된다(멱살 잡힌 사람이 걸린 거임).
2. (일반모드) 다 돌아가면 술래부터 아무나 찍으면서 그의 번호를 부른다.
2. (하드모드) 다 돌아가면 술래부터 아무나 찍으면서 아무 번호나 부른다.
3. (일반모드) 찍힌 사람이 다시 아무나 찍으면서 그 사람의 번호를 부른다.
3. (하드모드)  '찍힌(번호 불린 사람 말고)' 사람이 다시 아무나 찍으면서 아무 번호나 부르되 방금 자기를 찍은 사람 번호를 부르면 안 된다. 그럴 경우 번호 불린 사람은 자기가 아까 찍었던 사람의 멱살을 잡고 "나는 뭔데?!"를 외쳐주면 된다. 번호 불린 사람이 다시 누굴 찍으려고 하면, 안 틀린 사람이 일어나서 틀린 사람 멱살을 잡고 "나는 뭔데?!"를 외쳐준다.
4. 걸리는 경우는 다음과 같다.
4. (일반모드) 누군가의 번호를 부르며 찍는데 번호가 틀릴 때(찍힌 사람이 일어나 자길 찍은 사람 멱살을 잡는다), 혼자 "쟤 틀린 거 아냐?"라고 반문하고 있을 때(옆사람이 그 얼간이의 멱살을 잡아주며 "쟤는 뭔데?!"라고 한다).
4. (하드모드) '번호를 불린' 사람이 혼자 누굴 또 찍고 있을 때, 혼자 누가 누군지 몰라 얼때릴 때.
5. 게임의 목적은 다양한 정체성을 실험해 보는 청년기의 질풍노도를 놀이로 체험하면서 자신의 정체성을 파악하고 다른 사람의 정체성을 이해하는 훈련을 하는 데 있는 게 아니라 그냥 미친듯이 헷갈리게 놀아보는 데 있다.
9. 너무 어려워서 개선이 필요하다.

Posted by 엽토군

댓글을 달아주세요:: 네티켓은 기본, 스팸은 사절


카테고리

분류 전체보기 (781)
0 현재 호주워홀 (6)
1 내 (324)
2 다른 이들의 (251)
3 늘어놓은 (34)
4 생각을 놓은 (68)
5 외치는 (69)
9 도저히 분류못함 (28)

달력

«   2018/04   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          
Statistics Graph