Kichul’s Blog
Welcome 👋
Welcome 👋
작성자가 평상시에 실행한 git 명령을 설명한다. 대다수의 git 사용자는 전혀 필요하지 않을 수 있다.
wasavi는 크롬, 오페라, 파이어폭스 확장이다. 웹 페이지의 TEXTAREA 요소를 VI 에디터로 바꿔준다.
사용 방법은 간단하다. 확장 프로그램을 설치하고, TEXTAREA에 포커스를 준다. 이 상태에서 CTRL+ENTER를 누르면 wasavi가 활성화된다.
IAB1의 기술 연구소에서 UID2라는 시스템을 연구하고 있다. UID2는 사용자가 추적을 피하려고 무엇을 하고, 어떤 조치를 하든 상관없이 사용자를 추적하는 더 진보된 방법이다.
UID2는 이메일 주소와 사용자 정보를 엮는다. 프라이버시에 예민한 사용자는 서비스마다 이메일 주소를 변경하는 것으로 이러한 추적을 막는다. 예를 들면 지메일 주소가 Han.Solo@gmail.com이면 H.ansolo@gmail.com이나 ha.ns.ol.o@gmail.com을 사용할 수 있기 때문이다. 나중에 스팸을 받게 되면 유출이나 개인 정보가 거래된 것을 알 수 있다.
UID2 API에서는 사용자 이메일 주소를 일반화하는 방법을 구체적으로 기술했다. h.a.n.solo+iab@gmail.com이 hansolo@gmail.com이 되는 것을 의미한다.
작년에 이 글의 작성자가 사용자의 프라이버시를 존중하고, 이런 변경을 다시 되돌려 달라는 요청을 했다. 2 이 변경은 궁극적으로 유효하지만 추가하고 싶은 변경은 아니라는 답변을 받았다.
다음 URL을 브라우저에서 열었을 때와 curl로 열었을 때의 결과가 다르다. 이유는 HTTP의 빌트인 “content negotiation” 때문이다.
다음은 구글 크롬에서 요청할 때의 accept 헤더이다.
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
CURL의 요청 헤더는 훨씬 간단하다.
accept: /
csvbase 사이트의 기본 포맷이 CSV이므로 CURL 요청에는 CSV로 응답한다. 반면에 구글 크롬은 요청대로 HTML을 응답한다.
PNG 1.0 스펙은 1996년도에 발표했다. 인기의 비결은 다음과 같다.
이 글에서는 2022년 10월에 발표한 PNG 스펙 초안 세 번째 판을 참고했다. PNG 파일 쓰기, PNG Chunks와 같은 것을 설명한다. 주로 1.0 스펙에 해당한다.
The Interactive Advertising Bureau. 미국인터넷광고협회 ↩︎
Socketify.py는 고성능, 신뢰성 있는 파이선 웹 프레임웍으로 대규모 앱 백엔드와 마이크로 서비스를 만드는 용도이다.
Socketify.py 기능:
socketify.py가 초당 약 1.2메가 HTTP 요청이 되는 것으로 벤치마크 결과가 나타났다. GO의 fiber보다 더 나은 결과이다. 해커 뉴스에서 성능을 주제로 여러 가지 논쟁을 하고 있다.
Just는 프로젝트에 특화된 커맨드를 정의하고 실행할 수 있다. Make와 같은 역할을 한다. 맥, 윈도, 리눅스를 지원한다. 그뿐만 아니라 Make를 뛰어넘는 유용하고, 개선된 기능을 제공한다. VS Code, Emacs를 포함한 다양한 에디터를 지원한다.
alias b : build
host := `uname -a`
# build main
build:
cc *.c -o main
# test everything
test-all: build
./test --all
# run a specific test
test TEST: build
./test --test {{TEST}}
이렇게 사용할 수 있다.
$ just build
매뉴얼 내용은 충분한 것 같다. 해커 뉴스 평가도 좋다. 많은 플랫폼에서 설치 방법을 제공하는데, 가벼운 프로젝트에서 사용하기 좋을 것 같다.
nanoGPT는 중간 크기의 GPT를 훈련, 파인튜닝 하기 위한 가장 단순하고, 빠른 저장소이다.
이 저장소에 있는 train.py 파일은 OpenWebText 데이터세트1에서 GPT-2를 재현할 수 있다. 8 x A100 40GB 노드에서 실행하고, 훈련에 38시간 걸린다.
훈련에 상당한 자원이 필요하다. 이 프로젝트는 GPT-3에 비하면 교육용으로 시도할 정도 규모로 보인다.
원문 : SQLite Wasm in the browser backed by the Origin Private File System
SQLite는 서버리스 데이터베이스라서 따로 프로세스를 분리하지 않는 것이 특징이다. 파일 하나가 데이터베이스가 되는 구조이다.
모바일, 데스크톱 애플리케이션뿐만 아니라 웹에서도 SQLite를 사용할 수 있다. sql.js처럼 비공식적으로 Wasm2 기반의 SQLite가 있었지만, 공식 버전은 SQLite3 WASM/JS subproject가 처음이다.
SQLite3 WASM/JS subproject에는 다음 목표가 있다.
공식 홈페이지에서 WASM 압축 파일을 내려받고, 테스트할 수 있다.
OPFS Explorer 크롬 확장을 사용하면 SQLite Wasm이 OPFS에 쓰는 데이터 내용을 디버깅할 수 있다.
YouPlot은 터미널에서 차트를 그리는 커맨드 라인 도구이다. barplot, lineplot, histogram, scatter, density, boxplot, count 형태의 차트를 그릴 수 있다.
사용 방법은 다음과 같다. 터미널에서 얻은 RAW 데이터를 바로 차트로 만들 수 있다.
curl -sL https://git.io/ISLANDScsv \
| sort -nk2 -t, \
| tail -n15 \
| uplot bar -d, -t "Areas of the World's Major Landmasses"
OpenWebText는 HTML 페이지가 천만 건, URL이 2,300만 건이 넘는다고 한다. 이 데이터세트는 GTP-2 훈련에 사용되었다. ↩︎
Web Assembly ↩︎
다음 메타 태그를 추가하면 리퍼러를 서버에 전달하지 않을 수 있다.
<meta name="referrer" content="no-referrer" />
적용 전:
218.xx.xx.x - - [27/Oct/2018:02:35:08 +0000] “GET / HTTP/1.1” 200 38963 “http://localhost:8080/bookmark” “Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36” “-“
적용 후:
218.xx.xx.x - - [27/Oct/2018:02:33:24 +0000] “GET / HTTP/1.1” 200 38958 “-” “Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36” “-“
Dockerfile 작성할 때 참고용
FROM centos:7
RUN yum install -y make libtool pkgconfig autoconf python-sphinx wget bzip2 bind-utils
EXPOSE 53
COPY knot.conf /work
WORKDIR /work
ENTRYPOINT ["/entrypoint.sh"]
CMD ["python", "-u", "/work/app.py"]
Run
docker run -it --rm \
-p 3307:3306 \
-v /nfs/rt-knot-service/mysql/entrypoint.sh:/entrypoint.sh \
-e MYSQL_ROOT_PASSWORD=passwd \
my-docker-image:v1 \
/bin/bash
Kubernetes에서 자주 사용하는 커맨드를 정리했다.
# pod 보기
kubectl get pods
# namespace 만들기
kubectl create namespace my_namespace
# new_context라는 이름의 새로운 context 만들기
kubectl config set-context new_context \
--user=kubernetes-admin \
--cluster=kubernetes \
--namespace=my_namespace
# 새로운 context 사용하기
kubectl config use-context new_context
Python 애플리케이션에서 다음 에러가 발생했다.
Traceback (most recent call last):
File "/usr/bin/myapp", line 11, in <module>
sys.exit(run())
File "/usr/lib/python2.6/site-packages/click/core.py", line 716, in __call__
return self.main(*args, **kwargs)
File "/usr/lib/python2.6/site-packages/click/core.py", line 696, in main
rv = self.invoke(ctx)
File "/usr/lib/python2.6/site-packages/click/core.py", line 1060, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/lib/python2.6/site-packages/click/core.py", line 889, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/lib/python2.6/site-packages/click/core.py", line 534, in invoke
return callback(*args, **kwargs)
File "/usr/lib/python2.6/site-packages/myapp/cli.py", line 69, in worker
worker_server.main()
File "/usr/lib/python2.6/site-packages/myapp/worker/worker.py", line 35, in main
rcv_data = main_receiver.recv_json()
File "/usr/lib64/python2.6/site-packages/zmq/sugar/socket.py", line 399, in recv_json
msg = self.recv(flags)
File "socket.pyx", line 628, in zmq.backend.cython.socket.Socket.recv (zmq/backend/cython/socket.c:5616)
File "socket.pyx", line 662, in zmq.backend.cython.socket.Socket.recv (zmq/backend/cython/socket.c:5436)
File "socket.pyx", line 139, in zmq.backend.cython.socket._recv_copy (zmq/backend/cython/socket.c:1771)
File "checkrc.pxd", line 21, in zmq.backend.cython.checkrc._check_rc (zmq/backend/cython/socket.c:6032)
zmq.error.ZMQError: Interrupted system call
이 내용이 중요한 것 같다.
zmq.error.ZMQError: Interrupted system call
환경:
Centos 6에서는 문제가 없는 앱이었는데, Centos 5에서만 이런 문제가 생겼다. 그래서 trace를 하게 되었다.
app.py는 다음과 같이 하나의 스레드로 0mq 소켓으로 수신받고 있었다. strace를 하면 아래에 표시된 부분에서 에러가 발생했다.
...
receiver = context.socket(zmq.PULL)
receiver.bind("inproc://worker-receiver")
while True:
rcv_data = receiver.recv_json() # 여기서 에러 발생
다음과 같이 두 개의 터미널을 띄워놓고, 순서대로 커맨드를 실행했다.
# terminal 1
python app.py
# terminal 2
strace -ff -xx -i -p $(ps -anpL | grep app.py | awk '{print $1}')
try로 에러 코드를 확인했다.
except Exception as e:
print e
print e.errno
# 출력 결과
Interrupted system call
4
4번을 표준 errno 시스템 심볼을 확인하니 다음으로 나타났다.
errno.EINTR¶
Interrupted system call
Centos 6에서는 문제가 발생하지 않았다.
# Centos 5
strace -V
strace -- version 4.5.18
# Centos 6
strace -V
strace -- version 4.8
Centos 5에서는 strace 할 때 무엇인가 signal을 보내면서 문제가 발생하는 것으로 보인다. 그래서 우선 예외 처리하는 것으로 문제를 해결했다.
Python 스크립트가 예기치 않게 종료했을 때, 어떤 signal에 의해 종료되었는지 확인이 필요한 경우가 있다. 다음 코드를 변형해서 프로그램에 맞게, 적절히 적용할 수 있다.
import signal
# 각각의 signal에 대한 핸들러
def sighandler(signum, frame):
''' 시그널 처리 '''
raise Exception("Signal. %i" % signum)
# signal 수신 함수
def register_all_signal():
''' 모든 시그널 등록 '''
for x in dir(signal):
if not x.startswith("SIG"):
continue
try:
signum = getattr(signal, x)
signal.signal(signum, sighandler)
except:
# signal 등록에 실패하는 경우 표시
print('Skipping the signal : %s' % x)
continue
# signal을 수신하는 function 실행
register_all_signal()
이 외에도 종료되는 시점에 무엇인가 실행할 필요가 있을 때는 atexit를 사용할 수 있다.
import atexit
def at_exit_func():
print "exited."
atexit.register(at_exit_func)
Virtualbox에 두 개의 가상서버로 Kubernetes 설치하는 방법이다.
두 서버에 Kubernetes와 docker와 같은 기본 패키지를 설치한다.
# repository 추가
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
# docker, kubeadm 설치
apt-get update
apt-get install docker.io apt-transport-https kubelet kubeadm kubectl
# swap off
swapoff -a
호스트 설정:
# vi /etc/hosts
192.168.0.101 k8s1
192.168.0.102 k8s2
init이 정상적으로 실행되면, 다른 노드에서 join을 할 수 있는 명령어가 출력되고, 노드 서버에 그대로 입력하면 된다.
kubeadm init \
--apiserver-advertise-address=192.168.0.101 \
--pod-network-cidr=10.244.0.0/16
mkdir -p $HOME/.kube
rm -f $HOME/.kube/config
rm -f /home/ubuntu/config
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo cp -i /etc/kubernetes/admin.conf /home/ubuntu/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
sudo chmod 755 /home/ubuntu/config
# 네트워크 카드 설정에 따라 적절히 변경 필요 - 이 글 마지막 참조
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml
kubeadm join --token 037b45.73668ffc35fda768 192.168.0.101:6443 --discovery-token-ca-cert-hash sha256:126e83fa2c0b660e745d1eb225efaba55ebad93d3e0445c4065fc6637e652087
mkdir -p $HOME/.kube
scp ubuntu@192.168.0.101:/home/ubuntu/config $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Virtualbox에서 NAT, Local 두 개의 네트워크 인터페이스를 사용하는데, NAT 네트워크가 첫 번째라서 Flannel이 비정상적으로 동작했다. Local 네트워크 enp0s8을 사용하도록, 다음 파일을 내려받고 수정해야 한다.
https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml
command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr", "--iface=enp0s8" ]
# 버전 확인
db.version()
# db 보기
show dbs
# db 선택
use dbname
# 콜렉션명 가져오기
db.getCollectionNames()
# 컬렉션 데이터 가져오기
db.collection_name.find()
# 역순으로
db.collection_name.find().sort({"_id", -1})
db.collection_name.find({}, {"sort": [["_id", "-1"]]})
# 갯수 가져오기
db.collection_name.count()
# 백업
mongodump --db dbname --out ./mongo_backup_dir
# 복원
mongorestore --db dbname --drop ./mongo_backup_dir
# 모니터링
mongostat
NVM은 여러 Node.js 버전을 사용할 수 있게 하는 CLI 도구이다.
# 설치하기
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
# NVM 환경 불러오기
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
셸을 재접속해도 NVM을 찾을 수 없을 때는 직접 .bashrc에 NVM 환경 불러오기 부분을 추가하면 된다.
# LTS 버전 설치
nvm install --lts
# LTS 버전 사용하기
nvm use --lts
# 현재 버전 확인하기
node --version
RVM은 여러 Ruby 버전을 사용할 수 있게 관리하는 CLI 도구이다.
다음과 같이 간단하게 설치하고, 셸에 다시 접속하면 RVM을 사용할 수 있다.
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s stable
# ruby 특정 버전 설치하기
rvm install 2.0.0
# 특정 버전 사용하기
rvm use 2.1.0
# 특정 버전 사용하고, 기본으로 설정
rvm use --default 2.1.0
윈도를 사용하면서 알게 된 팁과 커맨드를 정리했다.
리눅스 커맨드 대체:
# ps와 같은 커맨드
tasklist
# kill과 같은 커맨드
taskkill
# which, whereis
where
다음 디렉터리에 바로가기를 위치시킨다.
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
또는 레지스트리에서 경로를 추가한다. ShellEnhanced라는 프로그램은 시작 프로그램으로 등록이 되지 않았는데, 이 방식으로 해결했다.
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run
서브라임텍스트 에디터에서 Sublimelinter를 사용한 각 언어에 대한 설치와 설정 방법에 대해 알아본다. 이 문서는 앞으로 업데이트가 계속될 예정이다.
gem install mdl
서브라임텍스트에서 SublimeLinter-contrib-mdl 설치한다. 윈도는 바로 Linter가 작동하는데, OSX는 재시작이 필요하다.
이렇게만 해도 Linter는 동작한다. 그런데 글이 설정한 Wordwrap보다 길어지면 MD013 Rule에 걸린다. 그래서 이것을 제외할 필요가 있다.
먼저 커맨드 팔레트에서 다음을 선택한다.
SublimeLinter Settings - User
그리고 다음과 같이 설정한다.
{
"user": {
...
"linters": {
...
"mdl": {
"@disable": false,
"args": ["--rules","all","--rules","~MD013"],
"excludes": []
}
},
}
}
링크:
{
"user": {
"debug": false,
"delay": 0.25,
"error_color": "D02000",
"gutter_theme": "Packages/SublimeLinter/gutter-themes/Default/Default.gutter-theme",
"gutter_theme_excludes": [],
"lint_mode": "background",
"linters": {
"coffeelint": {
"@disable": false,
"args": [],
"excludes": []
},
"cpplint": {
"@disable": false,
"args": [],
"excludes": [],
"filter": "",
"linelength": ""
},
"eslint": {
"@disable": false,
"args": [],
"excludes": []
},
"htmlhint": {
"@disable": false,
"args": [],
"excludes": []
},
"jshint": {
"@disable": true,
"args": [],
"excludes": []
},
"json": {
"@disable": false,
"args": [],
"excludes": [],
"strict": true
},
"pep8": {
"@disable": true,
"args": [],
"excludes": [],
"ignore": "",
"max-line-length": null,
"select": ""
},
"php": {
"@disable": false,
"args": [],
"excludes": []
},
"phplint": {
"@disable": false,
"args": [],
"excludes": []
},
"pylint": {
"@disable": false,
"args": [],
"disable": "",
"enable": "",
"excludes": [],
"paths": [],
"rcfile": "",
"show-codes": false
},
"shellcheck": {
"@disable": false,
"args": [],
"exclude": "",
"excludes": []
}
},
"mark_style": "outline",
"no_column_highlights_line": false,
"passive_warnings": false,
"paths": {
"linux": [],
"osx": [],
"windows": [
"C:/Program Files/php-5.4.43"
]
},
"python_paths": {
"linux": [],
"osx": [],
"windows": []
},
"rc_search_limit": 3,
"shell_timeout": 10,
"show_errors_on_save": false,
"show_marks_in_minimap": true,
"syntax_map": {
"html (django)": "html",
"html (rails)": "html",
"html 5": "html",
"php": "html",
"python django": "python",
"r extended": "r"
},
"warning_color": "DDB700",
"wrap_find": true
}
}
원격 SSH 서버에 비밀번호 없이 키를 통해 접속하는 방법에 대해 알아보겠다. 간단하지만 실제로 자주 사용한다.
로컬 서버에서 키 생성:
# 경로, 비밀번호 모두 엔터
ssh-keygen -t rsa
# 공개키 복사
cat ~/.ssh/id_rsa.pub
원격 서버에 공개키 추가:
# 원격 서버 접속
ssh user_id@server_address
# 공개키 추가. 공개키 붙여넣기 후 EOF 입력하고 엔터
cat >> ~/.ssh/authorized_keys << EOF
이제 로컬 서버에서 원격 서버로 비밀번호 없이 접속이 가능하다.
윈도에서는 SecureCRT, Putty를 사용해서 터널링 기능을 사용했고, OSX, 리눅스에서는 기본적으로 ssh가 있어서 ssh로 터널링 기능을 가끔 사용했다.
작업하는 플랫폼에 따라서 통일되지 않아서 윈도에서 Cygwin을 설치해서 ssh를 사용하기로 했다. 즉, 터널링 기능은 ssh를 통해서만 하기로 했다.
이제 ssh를 사용한 터널링에 대해서 알아보겠다.
한 서버에 접속하고, SOCKS5로 1080 포트를 여는 예제이다. 웹브라우저에서도 이 포트를 통해 웹브라우징이 가능하다.
ssh -D 1080 -f -C -q -N user_id@server_address
위에서 만든 프락시를 사용해서 또 다른 서버로 접속하는 방법이다.
ssh -o "ProxyCommand nc -X 5 -x localhost:1080 %h %p" user_id@server_address
PC, A 서버, B 서버가 있다고 가정한다. PC에서 B 서버로 직접 접속이 막혀있을 때, A 서버를 프락시로 만들어 위의 커맨드로 PC에서 B 서버로 접속이 가능한 것이다.
외부로 공개되지 않은 포트에 접속하는 방법이다.
다음은 GUI 도구에서 서버의 MySQL에 접속할 때 사용하는 예제다.
ssh -L localhost:3306:localhost:3306 user_id@server_address
중계 서버를 통해 다른 서버의 포트를 포워딩할 수도 있다.
ssh -L localhost:3306:another_server_address:3306 user_id@server_address
PC에 있는 포트를 원격 포트에서 사용할 때 사용하는 방법이다.
다음은 원격 서버에서 PC에 있는 MySQL에 접근하기 위한 명령이다.
# 리모트 포워딩
ssh -R 127.0.0.1:3306:127.0.0.1:3306 user_id@server_address
위에서 설명한 명령을 매번 입력하기 어렵기 때문에 자주 사용하는 경우는 설정 파일을 사용하는 것이 좋다.
설정 파일 도움말:
man ssh_config
다음과 같이 설정할 수 있다.
Host Socks5Example
HostName server_address
Port 22
user user_id
DynamicForward 1080
Host ProxyExample
HostName server_address
Port 22
User user_id
ProxyCommand nc -X 5 -x 127.0.0.1:1080 %h %p
Host LocalForwardExample
HostName server_address
Port 22
User user_id
LocalForward 127.0.0.1:3306 127.0.0.1:3306
Host RemoteForwardExample
HostName server_address
Port 22
User user_id
RemoteForward 127.0.0.1:3306 127.0.0.1:3306
Docker의 Centos6 이미지를 사용해서 Gitlab을 설치했다.
설치할 때 Gitlab 다운로드 페이지를 참고했다.
# 요구 패키지 설치
yum install curl openssh-server openssh-clients postfix cronie
service postfix start
chkconfig postfix on
# http, SSH 포트 열기. Docker라서 lokkit 사용 안 함.
# lokkit -s http -s ssh
# 다운로드, 설치
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
sudo yum install gitlab-ce
# 설정 작업
sudo gitlab-ctl reconfigure
그런데 이런 에러가 발생했다.
Error executing action `run` on resource 'execute[load sysctl conf kernel.sem]'
docker를 실행할 때 privileged 옵션을 추가했다.
docker run \
--detach=true \
--privileged=true \
--interactive=true \
--tty=true \
--publish=80:80 \
--volume=`pwd`:/myvol \
--name=gitlab \
kichul/gitlab
이번에는 ruby_block[supervise_redis_sleep] action run에서 멈추는 문제가 발생했다.
그래서 다음 서비스를 실행하고, 다시 reconfigure를 실행하니 잘 설치됐다.
/opt/gitlab/embedded/bin/runsvdir-start &
http://localhost:80로 접속해서 초기 비밀번호를 지정했다. 초기 관리자 이메일 주소는 admin@example.com이었다.
Docker를 사용하면서 자주 사용하는 내용을 정리했다.
# 머신 시작하기
docker-machine start MachineName
# 환경 변수 지정
eval "$(docker-machine env MachineName)"
# 머신 업그레이드
docker-machine upgrade MACHINE_NAME
# Dockerfile로 빌드하기
docker build -t ImageName .
# 이미지를 컨테이너로 만들기
docker run \
--detach=true \
--privileged=false \
--interactive=true \
--tty=true \
--publish=80:80 \
--volume=`pwd`:/myvol \
--name=ContainerName \
--entrypoint=/bin/bash \
--add-host="gitlab:192.168.99.100" \
ImageName
# 컨테이너 커맨드 실행
docker exec -i -t ContainerName /bin/sh
# 이미지 리스트
docker images
# 실행 중인 컨테이너 리스트
docker ps
# 모든 컨테이너 리스트
docker ps -a
# 컨테이너 삭제하기
docker rm ContainerName
# 이미지 삭제하기
docker rmi ImageName
# 태그로 이미지 삭제하기
docker rmi ImageName:Tag
# 컨테이너를 이미지로 저장
docker commit Container ImageName:TagName
# 이미지 가져오기
FROM centos:6.6
# 파일을 복사
COPY FILE_NAME /CONTAINER_PATH
# 커맨드 실행
RUN yum install -y gcc
# 작업 디렉터리
WORKDIR /tmp
# --entrypoint으로 이 설정을 오버라이드 할 수 있음.
ENTRYPOINT ["/usr/local/bin/gitlab-startup.sh"]
가상 아이피를 통해 접근한다.
# 가상 아이피 확인
docker-machine ip default
192.168.99.100
# 가상 아이피로 접근
curl -XGET http://192.168.99.100
또는 포트 포워딩을 사용한다.
우선 Virtualbox에서 8000 -> 8000으로 포트 포워딩을 설정한다. 다음 커맨드를 입력한다.
# SSH 포트 포워딩 사용
sudo ssh user_id@localhost -L \*:80::8000
크롬을 조금이라도 가볍게 사용하려고, 정말 필요한 플러그인만 제외하고, 모두 삭제했다. 간단하게 익스텐션에 대한 코멘트를 남겼다. 이 페이지는 지속해 업데이트할 예정이다.
사용 중인 플러그인:
삭제한 플러그인:
윈도를 사용할 때, 프로그램을 설치하고, 사용하지 않는 프로그램이 많다. 파일 크기도 문제지만, 서비스에 등록되는 경우도 있어서 메모리와 CPU 자원을 차지하는 경우도 발생했다. 그래서 필요 없는 프로그램을 제거하면서 기록하기로 했다. 혹시 나중에 필요할 수도 있으므로 이 페이지에 기록하고, 지속해 업데이트할 계획이다.
기본적으로 chocolatey를 통해 프로그램을 설치한다. 그래서 choco를 통해 설치된 프로그램을 확인했다.
다음 목록은 필요 없는 프로그램을 모두 삭제하고, 남은 프로그램이다.
choco list -l
autoit 3.3.12.0 - 오토잇. 자동화 스크립트
chocolatey 0.10.3 - chocolatey 프로그램 자체
cmder 1.3.2 - 터미널 프로그램. cmd.exe, Powershell 대체
cyg-get 1.2.1 - cygwin 프로그램 가져오는 프로그램.
Cygwin 2.3.0 - cygwin. 윈도에서 리눅스 환경을 사용할 수 있음.
dropbox 3.10.11 - Dropbox
easy.install 18.4.0.20151024 - python easy install
ffmpeg 2.7 - ffmpeg 프로그램. 동영상 변환.
filezilla 3.14.1 - ftp, sftp 도구
Firefox 42.0 - 웹브라우저
gimp 2.8.14.20151006 - 이미지 작업. 포토샵 대체
golang 1.5.1 - GO 언어
google-chrome-x64 46.0.2490.86 - 웹브라우저
GoogleChrome.Canary 28.0.1461.0 - 웹브라우저
kdiff3 0.9.98 - diff 도구. git과 연동해서 사용 중
nanumfont 3.0 - 나눔 폰트
nsis.install 3.0-beta2 - 윈도 설치프로그램 만들 때 필요한 프로그램
PhantomJS 2.0.0 - phantomjs. headless 웹브라우저.
PHP 7.0.3 - PHP 언어
pip 1.2.0 - Python pip
putty 0.66 - putty. ssh 접속
putty.portable 0.66 - putty. ssh 접속
python2 2.7.10 - Python 언어
R.Project 3.2.1 - R 언어
redis-desktop-manager 0.7.5.1 - redis GUI 도구
RoboMongo 0.8.4.20140317 - MongoDB Gui 도구
ruby 2.2.4 - Ruby 언어
spacesniffer 1.2.0.2 - 사용 중인 하드디스크 크기 보는 프로그램
SQLite 3.15.2 - SQLite
sqlite.shell 3.10.1 - sqlite 셸 프로그램
sqliteadmin 0.8.3.201 - sqlite
sqlyog 12.09 - MySQL GUI 도구
SublimeText3 3.0.0.3083 - 에디터
tortoisesvn 1.9.2 - 소스 버전 관리 SVN.
virtualbox 5.0.10.104061 - 가상머신
WinPcap 4.1.3.20141005 - 패킷 캡처 라이브러리.
wireshark 1.12.8 - 패킷 분석 프로그램
다음은 필요 없어서 삭제한 프로그램 목록이다.
모두 chocolatey의 uninstall 기능으로 삭제했다.
# 관리자 권한으로 셸 실행 #
choco uninstall freecommander Atom baretail bowery codeblocks cpu-z dexpot dia Emacs f.lux fiddler flashdevelop freemind ietester nmap Opera Pencil sandboxie.install StarUML TotalCommander vagrant VisualStudioCode pgadmin3 Graphviz git.install launchy kodi
choco uninstall jmeter jdk8 jre8 javaruntime maven DotNet4.5.1 vcredist2005 vcredist2008 vcredist2010 vcredist2012
# 레지스트리 삭제 필요 - HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
choco uninstall Growl
그리고 삭제 실패한 프로그램이 있다. 의존성이 필요한 것이라서 그런데, 이런 프로그램은 그대로 두는 것이 좋을 것 같다.
삭제 실패한 프로그램:
choco uninstall autohotkey.portable
설치된 프로그램:
이름 | 게시자 | 설명 |
---|---|---|
Adobe Acrobat Reader DC - Korean | Adobe System Incorporated | PDF 리더 |
AMD Catalyst Install Manager | Advanced Micro Devices, Inc. | 그래픽 드라이버 |
Arduino | Arduino LLC | 아두이노 IDE |
ATnotes Version 9.5 | Thomas Ascher | 포스트잇 프로그램 |
CloudBerry Explorer for Amazon S3 5.0 | CloudBerryLab | S3 Gui |
Composer - PHP Dependency Manager | getcomposer.org | PHP 라이브러리 의존성 관리 도구 |
EditPlus (64bit) | ES-Computing | 에디터 |
Git version 2.5.0 | The Git Development Community | git client |
IIS Express Application Compatibility Database for x64 | - | 로컬과 원격 DB 관리 |
IIS Express Application Compatibility Database for x86 | - | 로컬과 원격 DB 관리 |
Inkscape 0.9.1 | inkscape.org | Illustrator 대체 |
Intel(R) Hardware Accelerated Execution Manager | Intel Corporation | 인텔 가상화 기술을 사용하는 가상화 엔진(hypervisor) |
Intel(R) Network Connections 21.0.504.0 | Intel | 네트워크 드라이버 |
Kana Reminder 1.5 | Kana Solution | 특정 시간에 알림 |
NuonSoft ShellEnhancer 3.0 | Nuonsoft | 단축키, 핫 코너 등 기타 부가 기능 |
NVM for Windows 1.1.1 | Ecor Ventures LLC | node 버전 관리 도구 |
R for Windows 3.2.2 | R Core Team | R 언어 |
Realtek High Definition Audio Driver | Realtek Semiconductor Corp. | 오디오 드라이버 |
Slack | Slack Technologies | 채팅 |
VanDyke Software SecureCRT 5.5 | VanDyke Software, Inc | ssh 접속 |
압축 시대 | kippler@gmail.com | 압축 프로그램 |
인텔(R) HD 그래픽 드라이버 | Intel Corporation | 그래픽 드라이버 |
인텔(R) 관리 엔진 구성요소 | Intel Corporation | - |
카카오톡 | Kakao Corp. | 메신저앱 |
포토스케이프 | - | 이미지 뷰어 |
삭제한 프로그램:
이름 | 게시자 | 설명 |
---|---|---|
npEfdsWCtrl | INCA Internet Co., Ltd | 인터넷뱅킹 |
Blender | Blender Foundation | 3D 그래픽 프로그램 |
Jenkins | Jenkins Project | CI 툴 |
Opera | Opera Software | 웹브라우저 |
Android Studio | Google Inc | 개발 IDE |
TightVNC | GlavSoft LLC | VNC 클라이언트, 서버 |
Heroku Toolbelt 3.42 25 | Heroku, Inc | Heroku 도구 |
Microsoft OneDrive | Microsoft Corporation | 클라우드 서비스 |
Bacular System(R) Enterprise | - | 백업 시스템 |
AWS Command Line Interface | Amazon Web Service Developer Relations | AWS CLI 도구 |
MongoDB 3.4.2 2008R2Plus SSL (64bit) | MongoDB | MongoDB 서버, 클라이언트. 필요한 서버에 직접 설치하는 것이 좋을 것 같음. |
VistaSwitcher | NTWind Softwre | Alt+Tab 대체. 모니터별로 따로 관리 가능 |
Docker Toolbox version 1.11.2 | Docker | 개발 서버에서 작업하면서 사용 안 함. |
pgAdmin3 1.22 | The pgAdmin Development Tem | Postgresql GUI Tool |
Java 8 Update 121 (64bit) | Oracle Corporation | - |
Java 8 Update 121 | Oracle Corporation | - |
Java SE Development Kit 8 Update 101 (64bit) | Oracle Corporation | - |
Java SE Development Kit 8 Update 111 (64bit) | Oracle Corporation | - |
Java SE Development Kit 8 Update 121 (64bit) | Oracle Corporation | - |
Java SE Development Kit 8 Update 60 (64bit) | Oracle Corporation | - |
Java SE Development Kit 8 Update 92 (64bit) | Oracle Corporation | - |
Java(TM) 6 Update 20 | Sun Microsystems, Inc | - |
Ruby 2.1.7-p400 | RubyInstaller Team | Ruby 언어 |
PHP 5.3.1 | The PHP Group | PHP 언어 |
Git version 2.6.3 | The Git Development Community | git client |
네이트온 | SK Communications | 메신저 |
OCaml | Inria | 언어 |
WinCDEmu | Bazis | ISO 파일 마운트 |
Node.js | Joent, Inc and other Node contributors | Node.js |
WinPDF Writer | TopByteLabs Ltd. | PDF로 인쇄 |
Cisco LEAP Module | Cisco Systems, Inc, | - |
Cisco EAP-FAST Module | Cisco Systems, Inc, | - |
Cisco PEAP Module | Cisco Systems, Inc, | - |
윈도 - 실행 - %appdata% 입력
nProtect > npEfdsWCtrl 디렉터리 삭제 후 제어판에서 찾고, 삭제한다.
regedit
실행 후 다음 레지스트리에 있는 tvncontrol을 삭제한다.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
설치된 프로그램:
삭제한 프로그램:
regedit
다음 레지스트리를 삭제한다.
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows Kit
설치된 프로그램:
삭제된 프로그램:
설치된 디렉터리를 휴지통에 버린다.
시작 프로그램에서 제거:
WIN+R -> shell:startup 입력 -> 불필요한 프로그램 삭제
제어판 프로그램 제거에서 삭제하는 방법
regedit
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall 에서 프로그램 찾아서 삭제
자동 업데이트를 하지 않는다. 그래서 시작프로그램에서 제거했다.
regedit
다음 레지스트리에서 Google Update가 있으면 삭제한다.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\MSConfig\startupreg (사용 안 하는 프로그램)
또는 시작 프로그램에 있는지 확인
cd C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
dir
업데이트는 NINITE로 해서 이건 필요 없다. 그래서 중지했다.
sc stop AdobeARMservice
TortoiseSVN -> 설정 -> Icon Overlays
Status cache를 None으로 설정
사용하지 않는 서비스를 제거하는 방법에 대해 다룬다.
sc stop IMGSF50_Svc
sc config IMGSF50_Svc start= disabled
sc delete IMGSF50_Svc
rm C:\Windows\IMGSF50Svc.exe
서브라임텍스트에서 자주 사용하고, 유용한 팁을 정리했다.
에디터를 사용하다 보면 사이드바를 늘리고, 레이아웃을 4분할을 쓰게 되면 코딩영역이 작아져서 가로 스크롤이 생겨 불편한 경우가 있다. 이때 메뉴에서 워드랩을 설정해 불편함을 없앨 수 있다.
Menu : View - Word Wrap
Menu : View - Word Wrap Column - 70
가이드라인도 워드랩에 맞게 설정한다.
Menu : View - Rulers - 70
단축키:
(OSX) CMD + CTRL ↑ or ↓
(윈도) CTRL + SHIFT ↑ or ↓
블록을 지정해서 블록 단위로 위아래로 이동할 수도 있다.
커맨드 팔레트에서 다음 커맨드를 통해 정렬 기능을 사용할 수 있다.
다음 예제로 블록을 지정한 후 테스트할 수 있다.
AAA
DDD
CCC
BBB
AAA
단축키 :
(OSX) CMD + ALT + 숫자
(윈도) SHIFT + ALT + 숫자
숫자키 | 기능 |
---|---|
1 | 단독창 |
2 | Column 2개 |
3 | Column 3개 |
4 | Column 4개 |
5 | Grid |
8 (윈도 전용) | Row 2개 |
9 (윈도 전용) | Row 3개 |
(OSX) Row 2개와 Row 3개
CMD + ALT + SHIFT + 2
CMD + ALT + SHIFT + 3
단축키:
(OSX) CMD + SHIFT + L
메뉴:
Menu: Selection - Split Into Lines
또 하나의 팁. 선택 후 “ 또는 { 을 입력하면 선택영역이 해당 문자로 감싸게 된다.
현재 라인에서 커서 이후로 모두 지운다.
단축키:
(OSX) CMD + K + K or CMD + Delete
현재 라인에서 처음부터 커서까지 모두 지운다.
(OSX) CMD + Backspace
(윈도) Ctrl + Shift + P
(OSX) CMD + Shift + P
(윈도) CTRL + R
(OSX) CMD + R
(윈도) CTRL + P
(OSX) CMD + P
아이콘을 만들 때 다음 옵션을 줘서 이미 열린 서브라임텍스트 외에 추가로 빈 에디터를 띄울 수 있다.
"C:\Program Files\Sublime Text 2\sublime_text.exe" --new-window
(OSX) CMD + K + U
단축키:
(윈도) ALT + F3
(OSX) CTRL + CMD + G
해제는 ESC 또는 위 단축키를 다시 누르면 된다.
메뉴: View - Line Encodings - [Windows, Unix, Mac OS 9]
다음 파일 수정:
(윈도)
C:\Users\[Username]\AppData\Roaming\Sublime Text 2\Settings\Session.sublime_session
(OSX - Sublime Text 2)
OSX ▸ 사용자 ▸ kichuljung ▸ 라이브러리 ▸ Application Support ▸ Sublime Text 2 ▸ Settings
(OSX - Sublime Text 3)
vi ~/Library/Application\ Support/Sublime\ Text\ 3/Local/Session.sublime_session
Supervisor는 유닉스 계열의 시스템에서 여러 프로세스를 모니터링하고, 제어하는 프로그램이다.
여러 프로젝트를 진행할 때, 개발 서버를 환경에 따라 매번 세팅해야 했다. 이 프로젝트는 이 개발 서버, 저건 저 개발 서버, 프로젝트가 많아지면서 개발 서버가 분산되어 관리하기 어려웠다. Docker로 로컬 PC에서 환경을 돌려서 꽤 오랫동안 정착했었지만, 몇몇 문제점이 나타나게 됐다.
윈도 환경이라 docker-machine을 올리려면 Virtualbox를 써야 하는데, 자원이 부족한 경우가 많이 발생했다. 보안 업데이트 때문에 재부팅 하면 docker-machine 올리고, 컨테이너 올리고, 프로세스를 실행하는 작업을 해줘야 했는데, 꽤 귀찮았다. 그리고 개발할 때는 컨테이너가 실행되고, 자동으로 프로세스가 뜨도록 세팅하지 않아야 할 경우도 많았다.
그래서 원격의 한 개발 서버에서 모든 프로젝트를 작업할 수 있게 환경을 만들었다. 이때, Supervisor를 사용해서 스위치 on/off 하는 방식으로 프로세스를 관리하며 작업을 할 수 있게 만들었다.
Supervisor 설치하기 :
Python 모듈이라서 간단하게 설치할 수 있다.
# supervisor 설치
pip install supervisor
# 설정 파일 생성
echo_supervisord_conf > /etc/supervisord.conf
# 설정 파일 적절히 수정
vi /etc/supervisord.conf
# 데몬 실행
/usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf
이제 설정 파일을 어떻게 작성하는지 보겠다.
예:
기존 설정 파일 다른 부분은 수정하지 않고, 다음과 같이 파이선 프로그램을 추가했다.
[program:python_web_application]
command=python -m python_web_application.cli api
directory=/python_web_application_working_dir
autostart=true
키 | 설명 |
---|---|
command | 어떤 커맨드를 실행할지 정의 |
directory | 어떤 디렉터리에서 실행할지 정의 |
autostart | supervisor 데몬이 시작할 때 이 프로세스도 같이 실행할지 정의 |
이번에는 supervisorctl로 프로세스 관리하는 방법을 알아볼 것이다.
# supervisor 실행
supervisorctl
# 결과
project1_minio_sync RUNNING pid 25099, uptime 19:35:34
project1_react_app_es6 RUNNING pid 30815, uptime 6 days, 0:49:03
project1_zmq_app_api RUNNING pid 25407, uptime 18:34:25
project1_zmq_app_broker RUNNING pid 5631, uptime 4 days, 20:13:50
project1_zmq_app_worker RUNNING pid 18751, uptime 54 days, 0:57:26
...
supervisor> help
이렇게 실행하면 REPL 상태가 된다. 여기서 각종 커맨드를 사용할 수 있다. 위 예에서는 python 프로세스 3개, webpack 프로세스 1개, 파일 동기화하는 프로세스 1개를 보여주고 있다.
# 모든 프로세스 보기
status
# 설정된 모든 프로세스 보기
avail
# 나가기
exit or quit
# foreground로 프로세스를 확인
fg process_name
# 프로세스 시작, 중지, 재시작
start process_name
stop process_name
restart process_name
# supervisord를 재시작
reload
# 설정 파일을 다시 불러온다
reread
# 프로세스를 그룹에 추가
add process_name
현재 다음과 같은 프로세스를 사용하고 있다.
MySQL이나 Apache 등도 foreground로 실행하도록 추가하면 되지만 이미 서비스 스크립트가 있어서 따로 등록하지 않았지만, 이들도 Supervisor를 통해 관리할 수 있다.
production 환경에서도 사용하면 Supervisor를 통해 로그를 볼 수 있고, 프로세스를 관리할 수 있어서 편할 것 같다. 그전에 production에 사용한 사례가 있는지 확인은 필요할 것 같다.
Elasticsearch와 관련해서 참고할 만한 내용을 기록했다. 오래된 프로젝트를 다시 세팅하면서 썼고, Elasticsearch 버전은 1.0.0이다.
우선 Elasticsearch 다운로드 페이지에서 파일을 내려받는다.
# Java 설치
yum install -y java-1.8.0-openjdk
# 설치하기
cd /usr/local
unzip elasticsearch-1.0.0.zip
rm elasticsearch-1.0.0.zip
# 설정파일 수정 (cluster.name, node.name, node.rack, index.number_of_shards, index.number_of_replicas)
cd elasticsearch-1.0.0
vi config/elasticsearch.yml
# 데몬 실행
bin/elasticsearch
# 플러그인 목록 보기
bin/plugin --list
# 플러그인 설치 (head)
# http://localhost:9200/_plugin/head/
bin/plugin -install mobz/elasticsearch-head
# 버전 확인하기
curl -XGET http://localhost:9200/
# 검색
curl -XPOST http://localhost:9200/_search?pretty \
-H 'Content-Type: application/json;charset=UTF-8' \
-d '{
"from": 0,
"size": 100,
"query": {
"match": {
"_id": "gAfBdpp0SOWkNzOdIEXc1g"
}
}
}'
curl -XPOST http://localhost:9200/_search \
-H 'Content-Type: application/json;charset=UTF-8' \
-d '{
"from": 0,
"size": 100,
"query": {
"query_string": {
"query": ["_id:gAfBdpp0SOWkNzOdIEXc1g"]
}
}
}'
질의 문자열은 단어와 연산자의 연속으로 이뤄진다.
기본 형태는 다음과 같다. QUERY_STRING_SYNTAX라고 되어 있는 부분에 질의를 사용할 수 있다.
GET /_search
{
"query": {
"query_string": {
"query": "QUERY_STRING_SYNTAX"
}
}
}
Query String 문법 기본:
# status 필드에 active가 있을 때
status:active
# title에 quick 또는 brown이 있을 때
title:(quick OR brown)
# author 필드에 정확하게 "john smith"라는 문장이 있을 때
author:"John Smith"
# 와일드카드. ?는 문자 하나, *는 0개 이상의 문자들을 대체한다.
qu?ck bro*
# 정규식. //로 감쌈
name:/joh?n(ath[oa]n)/
# book.title, book.content, book.date이 quick 이나 brown을 포함하는 필드가 있을 때.
# 백슬래시로 *를 escape하는 것에 주의
book.\*:(quick brown)
이런 식으로 복잡한 질의도 사용할 수 있다.
curl -XPSOT http://192.168.0.106:9200/index_name/type_name/_search?pretty=true \
-H 'Content-Type: application/json;charset=UTF-8' \
-d '{
"from": 0,
"size": 10,
"fields": ["_id", "user_id", "user_name"],
"query": {
"query_string": {
"query": "foo_field.bar_field:/regexp_example/",
"analyze_wildcard": true,
"allow_leading_wildcard": true
}
}
}'
allow_leading_wildcard 옵션은 *이나 ?를 첫 번째 문자로 허용여부를 설정할 수 있다. 기본값은 True다. 정규식에서는 이 옵션의 영향을 받지 않는다. 다음과 같은 쿼리는 Elasticsearch가 인덱스의 모든 단어를 강제로 찾는다.
/.*n/
analyze_wildcard 옵션은 기본적으로 검색어에 있는 와일드카드를 분석하지 않는다. 세팅을 true로 하면 와일드카드를 분석하려고 최대한 노력하게 된다.
예를들어 다음과 같은 데이터가 있다.
{
files: [
{
...
"f_name": "C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe VV11.0.14.16VV"
...
},
...
]
}
f_name이 AcroRd32라는 단어와 VV11.0.14.16VV라는 단어가 모두 포함된 경우를 찾으려고 했다. 그래서 이런 식으로 검색하면 될 줄 알았다.
f_name:/.*AcroRd32 VV11.0.14.16VV/
안된다. 표준 토크나이저로 토큰화되므로 필드의 본래의 텍스트에 검색하는 것이 아니라 토큰화된 단어를 검색한다. 따라서 다른 토크나이저로 변경할 필요가 있다.
스냅숏 저장소를 만들고, 현재 상태를 스냅숏해서, 저장소 디렉터리에 보관이 되고, 이것을 다른 클러스터에서 복원 할 수 있다.
# 스냅숏 저장소 만들기
curl -XPUT http://localhost:9200/_snapshot/my_backup \
-H 'Content-Type: application/json;charset=UTF-8' \
-d '{
"type": "fs",
"settings": {
"compress": true,
"location": "/home/es_snapshot"
}
}'
# 등록된 스냅숏 저장소 보기
curl -XGET http://localhost:9200/_snapshot/_all
# 스냅숏 하기 - 끝날때까지 기다리기
curl -XPUT http://localhost:9200/_snapshot/my_backup/snapshot_1?wait_for_completion=true
# 스냅숏 상태 확인
curl -XGET http://localhost:9200/_snapshot/my_backup/snapshot_1
# 스냅숏 삭제하기
curl -XDELETE http://localhost:9200/_snapshot/my_backup/snapshot_1
# 스냅숏으로부터 복원
## 스냅숏한 디렉터리를 다른 클러스터로 가져와서 그 클러스터의 스냅숏 디렉터리에 둔다.
curl -XPOST http://localhost:9200/_snapshot/my_backup/snapshot_1/_restore
위에서 다른 클러스터로 복원했을 때, 리플리케이션과 샤드 세팅이 다르면 Unassigned 상태가 된다. 이런 경우 Unassigned 상태를 정상적인 상태로 만들고 싶을 때 reroute를 사용한다. reroute 커맨드는 클러스터가 reroute 할당 명령을 실행하도록 해준다.
스냅숏 했던 서버의 상태:
복원 할 서버 상태:
index를 만들지 않은 상태에서 복원해서 그럴 수도 있는데, 그건 테스트하지 않았다.
# 리플리케이션 조절
curl -XPUT localhost:9200/index_name/_settings \
-d '{
"number_of_replicas": 0
}'
# unassigned된 샤드를 재할당. 샤드를 다른 노드로 보낼 수도 있다.
curl -XPOST http://localhost:9200/_cluster/reroute \
-H 'Content-Type: application/json;charset=UTF-8' \
-d '{
"commands": [
{
"allocate": {
"index": "index_name",
"shard": 1,
"node": "node_name",
"allow_primary": true
}
}
]
}'
Javascript로 개발하면서 자주 사용하는 내용을 정리했다.
마우스 관련 :
이벤트 핸들러 | 설명 |
---|---|
onmouseenter | 포인터가 한 요소로 이동될 때 발생 |
onmouseleave | 포인터가 요소의 바깥으로 나갈 때 발생 |
onmousemove | 요소 위에서 포인터가 움직일 때 |
onmousedown | 요소에서 마우스 버튼을 눌렀을 때 발생 |
onmouseup | 요소위에서 마우스 버튼을 눌렀을 때 발생(release) |
자주 사용하는 이벤트 메소드 :
이벤트 핸들러 | 설명 |
---|---|
e.preventDefault() | 기본 기능이 동작하는 걸 막는다. |
e.stopPropagation() | 부모 노드 방향으로 이벤트가 전파되지 않도록 한다. |
var arr = [1,2,3];
// indexOf : 배열에 값이 존재하면 인덱스값을 리턴. 없으면 -1
arr.indexOf(1); // 0
arr.indexOf(4); // -1
// 배열에 원소 추가
arr.push(4); // [1,2,3,4]
// 배열에서 원소 제거
// 3의 인덱스를 구해서
var idx = arr.indexOf(3);
// 그 인덱스 값을 기준으로 1개 제거
arr.splice(idx, 1);
const files = ["F1", "F2", "F3"]
const result = files.map(file => {
return "- " + file;
});
console.log(result); // ["- F1", "- F2", "- F3"]
const foo = 'FOO';
const bar = 'BAR';
console.log(`${foo}-${bar}`); // FOO-BAR
// 유저 에이전트
navigator.userAgent
Generator는 빠져나갔다가 나중에 다시 돌아올 수 있는 함수다.
파일 업로드 처리하는데, 동시에 처리하는 파일의 개수를 제한할 필요가 있었다. while과 setInterval 조합으로 처리하려다가 좀 더 세련되고, 최근 기술을 사용하고 싶어서 Generator를 사용했다.
1번 예제는 써도 괜찮지만 2번 예제는 보완이 필요해 보인다.
다음은 Generator를 사용해서 비동기 함수를 순차적으로 실행하는 간단한 코드다.
// 실행하려는 비동기 함수
function PromiseFunc(i) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('returned', i)
resolve();
}, 1000);
});
}
// 이터레이터를 만드는 함수. *과 yield 사용
function* getIterator() {
for (let i=0; i<10; i++) {
yield PromiseFunc(i);
}
}
// 이터레이터를 가져온다.
const it = getIterator();
(function nextItem(val) {
// ret = {value: ..., done: true or false}
let ret = it.next(val);
// 이터레이터의 다음이 있는지?
if(!ret.done) {
ret.value.then(nextItem);
} else {
console.log("finished");
}
})()
이번에는 10개씩 동시 처리하는 예제다.
// 실행하려는 비동기 함수
function PromiseFunc(i) {
return new Promise((resolve) => {
console.log('invoked', i)
// 비동기 처리
setTimeout(() => {
console.log('returned', i)
resolve(i);
}, 1000);
});
}
// 이터레이터를 만드는 함수. *과 yield 사용
function* getIterator() {
for (let i=0; i<100; i++) {
yield PromiseFunc(i);
}
}
// 이터레이터를 가져온다.
const it = getIterator();
// 동시에 처리할 개수 지정
const CONCURRENT = 10;
// 카운트 초기화
let cnt = 0;
// 처리 시작
(function nextItem() {
let ret = it.next();
// 카운트 증가
cnt++;
if(!ret.done) {
ret.value.then(() => {
// 처리가 완료되어 카운트 감소
cnt--;
});
let timer = setInterval(() => {
if(cnt < CONCURRENT) {
nextItem();
clearInterval(timer);
}
}, 0);
} else {
console.log("finished");
}
})()
윈도에서 배치파일 작업을 하면서 자주 사용하는 코드를 정리했다.
REM 변수정의
SET TEST=foo
REM 변수 사용
echo %TEST%
call cmd /c start 배치파일명
%0(파일명), %1, %2, ...
test.bat:
echo %1 %2
다음과 같은 결과를 보인다.
# 실행
test.bat argu1 argu2
# 결과
argu1 argu2
bat 파일:
for %%Z IN (1,2,3,4) DO @echo %%Z
결과:
1
2
3
4
윈도 파워셸에서는 배치파일이 동작하지 않아서 cmd.exe를 사용하는 경우가 있다.
다음은 cmd.exe에서 잘 사용하는 옵션이다.
옵션 | 기능 |
---|---|
/K | 계속 남아있음 |
/C | 종료됨 |
# echo 를 실행하고, 다시 원래 셸로 돌아온다.
cmd /C echo test
# echo를 실행하고, 그 cmd 상태에 머문다.
cmd /K echo test
자주 사용하는 React Snippet을 모아놓은 페이지다.
constructor() {
}
// 최초 setState
componentWillMount() {
}
render() {
}
componentDidMount() {
}
// props이 변경되었을 때 여기서 setState 사용
componentWillReceiveProps(nextProps) {
}
// false로 리턴하면 이후 단계 진행안함.
shouldComponentUpdate(nextProps, nextState) {
}
componentWillUpdate(nextProps, nextState) {
}
render() {
}
componentDidUpdate() {
}
componentWillUnmount() {
}
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {withRouter} from 'react-router';
class ViewName extends React.Component {
static propTypes = {
dispatch: PropTypes.func
};
componentDidMount() {
const {dispatch} = this.props;
}
render() {
return (
<div>
here
</div>
);
}
}
export default connect(state => state)(withRouter(ViewName));
import React, {PropTypes} from 'react';
class ViewName extends React.Component {
static propTypes = {
};
render() {
return (
<div>
here
</div>
);
}
}
export default ViewName;
{cond ? (
<div>cond이 있을 때 이 컴포넌트를 보여줌</div>
) : null}
class Input extends React.Component {
input = null;
componentDidMount() {
this.input.focus();
}
render() {
return (
<div>
<input type="text" ref={(ref) => this.input = ref} />
</div>
)
}
}
PropTypes.func
PropTypes.array
PropTypes.object
PropTypes.string
PropTypes.number
PHP-FPM(FastCGI Process Manager)는 FastCGI 프로세스를 관리한다. 최초 실행했을 때의 프로세스를 재사용해서 FastCGI보다 더 빠른 처리를 할 수 있다. 이 페이지에서는 PHP-FPM을 설치하고, Nginx와 연동하는 방법을 다룬다.
Centos 6.7에서 PHP-FPM 설치:
# 설치
yum install php-fpm
# 설정파일 수정
vi /etc/php-fpm.conf
vi /etc/php-fpm.d/www.conf
# 데몬 실행
php-fpm --fpm-config /etc/php-fpm.conf
Nginx 설정:
server {
listen 80;
server_name localhost;
root /home/php-script;
charset utf-8;
location ~ \.php$ {
if (!-f $request_filename) {
return 404;
}
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
include fastcgi_params;
}
}
Nginx를 시작했다. PHP 파일 작성을 하고 테스트했다.
# 테스트 php 파일 작성
echo "<?php phpinfo(); " > /home/php-script/test.php
# 결과 확인
curl -XGET http://localhost/test.php
Centos 6.7에서 MySQL 5.5.27 버전 소스로부터 설치하는 과정을 기록했다.
우선 MySQL Community Server (Archived Versions) 페이지에서 5.5.27 파일을 내려받고, 설치하려는 서버에 올린다.
필요한 패키지 설치하기:
yum install -y cmake ncurses-devel
사전 작업:
# 사용자, 그룹 추가
groupadd mysql
useradd -r -g mysql -s /bin/false mysql
MySQL 빌드, 설치하기:
이미 다른 버전의 MySQL이 있는 상태라서 경로를 따로 지정해서 설치했다.
# 압축 풀고 빌드 디렉터리 생성
tar xvzf mysql-5.5.27
cd mysql-5.5.27
mkdir bld
cd bld
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql-5.5.27 ..
make
make install
설치 후 작업:
# 설치된 디렉터리로 이동
cd /usr/local/mysql-5.5.27
# 권한 조정
chown -R mysql .
chgrp -R mysql .
# 설정 디렉터리 생성, 파일 복사
mkdir etc
cp ./support-files/my-medium.cnf ./etc/my.cnf
# 데이터 초기화
scripts/mysql_install_db --user=mysql --basedir=. --datadir=./data
설정 파일 수정:
vi etc/my.cnf
my.cnf:
이미 MySQL이 설치되어 있어서 포트를 33067로 지정했다.
socket = /usr/local/mysql-5.5.27/tmp/mysql.sock
port = 33067
datadir=/usr/local/mysql-5.5.27/data
MySQL 데몬 실행:
이렇게 시작할 수도 있고, 뒤에서 다루는 서비스 스크립트로 만들 수도 있다.
./bin/mysqld_safe --defaults-file=./etc/my.cnf
루트 비밀번호 지정:
./bin/mysqladmin -u root -P 33067 password 'new password'
서비스 스크립트 수정:
# mysql.server 파일 init.d 디렉터리로 복사 후 수정
cp ./support-files/mysql.server /etc/init.d/mysqld5527
vi /etc/init.d/mysqld5527
기본 서비스 스크립트 파일은 디렉터리 설정이 약간 달라서 수정이 필요하다. 다음 네 부분을 찾아서 수정한다. (앞 단어로 검색, basedir, datadir, lock_file_path, mysqld_safe)
basedir=/usr/local/mysql-5.5.27
datadir=/usr/local/mysql-5.5.27/data
lock_file_path="$lockdir/mysql5527"
$bindir/mysqld_safe --defaults-file="$basedir/etc/my.cnf" --datadir="$datadir" --pid-file="$mysqld_pid_file_path" $other_args >/dev/null 2>&1 &
데몬을 시작한다.
service mysqld5527 start
이제 33067 포트를 사용해서 MySQL을 사용할 수 있다.
Python으로 개발하면서 작성한 메모이다. 기본적이지만 오랜만에 Python을 사용할 때 참고할 만한 내용을 적었다. Python 2.7 버전 기준이다.
A = 1
if A == 1:
print "1은 1이다."
else:
print "1은 1이 아니다."
try:
db_session.commit()
except InvalidRequestError as e:
# 여기에서 예외 처리하기
except Exception as e:
# 일반적인 예외 처리
raise e
import sys
print(sys.version_info)
SQLAlchemy는 Python에서 인기 있는 ORM 모듈이다.
Flask는 가벼운 Python 웹프레임웍이다. RESTful API를 아주 쉽고 간단하게 만들 수 있다.
Implementing API Exceptions 페이지에 아주 잘 나타나 있다.
우선 예외 클래스를 만든다.
from flask import jsonify
class InvalidUsage(Exception):
status_code = 400
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
어떤 API에서 예외가 발생했을 때 표시할 페이지의 응답 부분을 만든다.
@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
이제 API에서 예외가 발생했을 때, invalidUsage 클래스를 사용해서 예외를 전파하면 된다.
@app.route('/foo')
def get_foo():
try:
return some_action()
except Exception as e:
raise InvalidUsage('This view is gone', status_code=410)
a TypeError: Decimal('0.10') is not JSON serializable error
관련 Flask 이슈에서 simplejson을 설치하면 자동으로 jsonify가 그것을 사용한다고 했다.
# Centos 6.7
yum install -y python-pip
pip install --upgrade pip
# 설치된 python 모듈 모두 보기
pip list
# 특정 버전 설치
pip install jsonpatch==1.2
# 특정 모듈 제거
pip uninstall supervisor
Traceback (most recent call last):
File "/usr/bin/echo_supervisord_conf", line 5, in <module>
from pkg_resources import load_entry_point
File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 2655, in <module>
working_set.require(__requires__)
File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 648, in require
needed = self.resolve(parse_requirements(requirements))
File "/usr/lib/python2.6/site-packages/pkg_resources.py", line 546, in resolve
raise DistributionNotFound(req)
pkg_resources.DistributionNotFound: meld3>=0.6.5
setuptools를 설치한다.
sudo pip install --upgrade setuptools
nginx를 내려받고, 설치한다. 필요한 옵션을 추가한다.
# nginx 소스 설치
./configure \
--with-http_mp4_module \
--with-http_image_filter_module
make
sudo make install
/usr/local/nginx 디렉터리에 nginx가 설치됐다.
이제 재부팅 후에도 nginx를 실행될 수 있게, 서비스를 만든다. 다음과 같이 plist 파일을 하나 만든다.
/Library/LaunchDaemons/local.nginx.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>local.nginx</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/nginx/sbin/nginx</string>
<string>-g</string>
<string>daemon off;</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<false/>
<key>WorkingDirectory</key>
<string>/usr/local/nginx</string>
</dict>
</plist>
시작 서비스로 등록한다.
sudo launchctl load -w /Library/LaunchDaemons/local.nginx.plist
sudo launchctl start local.nginx
서비스를 해제하는 방법이다.
sudo launchctl stop local.nginx
sudo launchctl unload -w /Library/LaunchDaemons/local.nginx.plist
# 확인
sudo launchctl list
제거하기
# 지우기
sudo rm -rf /Library/LaunchDaemons/local.nginx.plist
sudo rm -rf /usr/local/nginx
Python, Flask 프레임웍으로 개발했던 서비스에서 월초만 되면 API에서 에러가 발생했다. DB는 MySQL, ORM은 SQLAlchemy를 사용했다. API 에러는 다음과 같았다.
ERROR in app: Exception on /XXXX/XXXX [POST]
...
OperationalError: (_mysql_exceptions.OperationalError) (1213, 'Deadlock found when trying to get lock; try restarting transaction') [SQL: u'INSERT INTO ...'] [parameters: (...)]
OperationalError 에러이고, 락을 가져오려고 할 때, Deadlock이 발견됐으니 트랜잭션을 다시 시작하라는 내용이다.
문제가 되는 부분을 찾았다. insert, commit 하는 부분이었다.
session.commit()
그래서 에러 메시지 내용에 따라 예외 처리를 했다.
try:
session.commit()
except Exception as e:
session.rollback()
raise e
이 상태에서 예외를 처리하지 않으면, 다시 말해서 rollback을 하지 않으면 이 API(세션)는 더 이상 DB Select조차 할 수 없다. 그때 발생하는 에러는 다음과 같다.
InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush.
Dead Lock 발생으로 서비스가 되지 않는 문제는 rollback으로 해결했다. 이제 Dead Lock이 걸리는 원인을 찾아야겠다.
일단 2개의 MySQL 이벤트가 있다.
특이사항:
그래서 개발 서버에서 재현하려고 시도했다. jMeter로 API에 Insert를 대량 발생시켰다. 그러나 에러는 발생하지 않았다.
여러 가지 설정도 바꿔봤지만, Dead Lock은 전혀 발생하지 않았다.
개발 서버와 실 서버의 mysql 버전 차이도 영향이 있을까?
실 서버에서는 발생하는데 어째서 개발 서버에서는 발생하지 않을까? 정확한 원인은 찾지 못했다. 이벤트에서 테이블 락을 걸어주고, 프로시저 작업이 끝나면 락을 풀고, 에러가 발생하는지 확인해봐야겠다.
Nginx Lua Module을 개발할 때 Shared Memory를 사용했다. 다음과 같은 함수로 Shared Memory를 다룰 수 있다.
그런데 한 가지 문제가 생겼다. 예를 들어
ngx.shared.DICT:add("1", 1)
ngx.shared.DICT:add("2", 1)
ngx.shared.DICT:add("3", 1)
ngx.shared.DICT:set("2", 2)
ngx.shared.DICT:get_keys()
이런 결과가 나타나길 기대했지만,
1 : 1
2 : 2
3 : 1
실제로 이런 결과가 나타났다.
1 : 1
3 : 1
2 : 2
순서는 그대로 두고, Shared Memory의 값이나 expires를 변경하고 싶었는데, 그렇게 되지 않았다.
replace 메소드를 확인했다.
set과 비슷하지만, 키가 존재할 때 ngx.shared.DICT 딕셔너리에 key-value 쌍을 보관한다. 이미 만료되었거나 딕셔너리에 키가 존재하지 않으면 success 리턴 값은 false가 되고, err 리턴 값은 “not found”가 된다고 했다. 따라서 replace는 해결 방법은 아니다.
모든 메소드를 확인해봤는데, 결국 해결 방법을 찾을 수 없었다. 그래서 구조를 다음과 같이 바꿨다.
이렇게 DICT_IDX라고, 새로운 공유 메모리를 추가하는 방식으로 해결했다.
이 글에서는 시스템의 상태를 확인할 때 자주 사용하는 커맨드와 툴을 알아본다. 일을 할 때 보통 Centos 계열을 많이 사용하고 다루는 편이다.
top보다 강력한 도구이다. 특정 패턴의 문자열에 일치하는 프로세스만 보여주는 Filter 기능과 부하나 메모리 순서대로 보여주는 Sort 기능을 자주 사용한다. 프로세스 상태를 보면서 바로 프로세스를 죽이는 기능도 있다. 또한 strace도 쉽게 확인할 수 있다. 버전에 따라 기능에 차이가 있으니 최신버전을 사용하는 것이 좋다.
# 설치
yum install -y htop
# 사용
htop
dstat은 CPU, Disk IO, 네트워크 트래픽을 확인할 때 사용한다.
# 설치
yum install -y dstat
# 사용
dstat
업데이트해야 할 내용
OSX에서 서브라임텍스트가 아주 느려지는 문제가 갑자기 발생했다. 텍스트를 입력하고, 방향키를 누르면 지연되는 증상이다. 그래서 원인을 찾으려고 몇몇 시도를 했다.
Sublimelinter 때문에 그럴 수도 있겠다 싶어서 delay 시간을 조절했다. 효과는 없었다. 설정의 문제가 아니라 어떤 플러그인에 의해서 발생하는 문제인 것 같다고 생각했다.
Preferences: Sublimelinter Settings
{
"user": {
"delay": 0.5,
...
...
}
이제 어떤 플러그인이나 설정이 문제를 발생하게 만드는지 알아보려고 했다.
우선 서브라임텍스트를 종료했다. 아래 디렉터리에서 두 개의 디렉터리를 다른 폴더로 이동했다.
~/Library/Application Support/Sublime Text 3
다시 서브라임텍스트를 열었고, 문제가 발생하지 않았다. 플러그인 문제다.
Installed Pacakges 디렉터리 안에 의심이 가는 플러그인을 넣어가면서 테스트 했다. 결국 Jekyll 플러그인을 넣으니 문제가 다시 발생했다.
더 자세히 알아봤는데, Jekyll 플러그인에서 설정 문제였는데 그 내용은 따로 글을 작성했다.
아무튼 플러그인이나 소프트웨어는 필요한 것만 써야겠다고 생각했다.
윈도 PC와 맥을 둘 다 사용한다. 윈도 PC는 성능이 좋아서 작업하면서 느리다고 생각한 적이 없는데, 맥에서 작업을 하면 너무 느려서 컴퓨터를 끄곤 했다. 특히 서브라임텍스트에서 텍스트를 입력하고, 방향키를 사용하면 3초 이상 지연이 발생한다. 한글을 입력했을 때, 이런 문제가 잘 발생했다. 에디터를 사용하지 못할 정도였다.
이 문제 외에도 부팅시간 후에 앱이 열리는 시간이 느린 문제도 있었다. 이런 문제가 있어서 최적화해야겠다고 생각했다.
가장 간단하게 할 수 있는 것이 사용하지 않는 프로그램을 삭제하는 것이다. 제거 소프트웨어를 사용하지 않았다. 기본적으로 응용프로그램 폴더에서 직접 휴지통으로 옮기는 방식으로 지웠고, 흔적들이 보이면 개별적으로 지웠다.
sudo rm -rf /usr/local/texlive
sudo rm -rf /Library/TeX
시스템 환경설정 - Tex Distribution - 오른쪽 마우스 보튼 클릭 후 패널 제거 선택
서비스 제거
sudo launchctl stop comp.text.tex.distribution.Helper
sudo launchctl unload -w /Library/LaunchDaemons/comp.text.tex.distribution.Helper.plist
sudo rm -rf /Library/LaunchDaemons/comp.text.tex.distribution.Helper.plist
베네수엘라 대란 때 샀을 때 설치했다가 환불하고 안 지웠었는데, 이제 지웠다.
# 서비스 제거
sudo launchctl unload -w /Library/LaunchDaemons/com.microsoft.office.licensingV2.helper.plist
sudo launchctl unload -w /Library/LaunchDaemons/com.microsoft.autoupdate.helper.plist
sudo rm -rf /Library/LaunchDaemons/com.microsoft*
sudo rm -rf /Library/PrivilegedHelperTools/com.microsoft*
plist만 남아있었다.
sudo launchctl unload -w /Library/LaunchDaemons/com.teamviewer.Helper.plist
rm -rf ~/Library/Preferences/com.teamviewer*
sudo rm -rf /Library/Preferences/com.teamviewer*
sudo rm -rf /Library/LaunchDaemons/com.teamviewer*
sudo rm -rf /Library/PrivilegedHelperTools/com.teamviewer.Helper
인터넷 뱅킹 관련해서 설치했다가 지웠는데, plist 파일이 남아있었다.
다음 방법으로 지웠다.
sudo launchctl list | grep nprotect
sudo launchctl unload -w /Library/LaunchDaemons/com.nprotect.kext.nProtectFW.plist
sudo launchctl unload -w /Library/LaunchDaemons/com.nprotect.nosintgdmn.plist
sudo rm -rf /Library/LaunchDaemons/com.nprotect.*
제거했는데, plist 파일이 남아있었다.
다음 방법으로 지웠다.
sudo launchctl list | grep realvnc
sudo launchctl unload -w /Library/LaunchDaemons/com.realvnc.vncserver.plist
sudo rm -rf /Library/LaunchDaemons/com.realvnc.vncserver.plist
# 확실하지 않음.
# /Library/vnc/vncserver_service_daemon
# /etc/vnc/service
rm -rf ~/Library/Application\ Support/Growl/
rm -rf ~/Library/Preferences/com.Growl.GrowlHelperApp.plist
잘 사용하지 않아서 지웠다. 지우는 방법은 다음과 같다.
# Application 제거
sudo rm -rf /Applications/Vagrant
sudo rm -f /usr/local/bin/vagrant
sudo pkgutil --forget com.vagrant.vagrant
# Data 제거
rm -rf ~/.vagrant.d/
수동으로 제거해야 한다. 가능하면 XQuartz를 요구하는 앱을 사용하지 않을 생각이다.
https://gist.github.com/pwnsdx/d127873e24cef159d4d603accaf37ee4
launchctl unload /Library/LaunchAgents/org.macosforge.xquartz.startx.plist
sudo launchctl unload /Library/LaunchDaemons/org.macosforge.xquartz.privileged_startx.plist
sudo rm -rf /opt/X11* /Library/Launch*/org.macosforge.xquartz.* /Applications/Utilities/XQuartz.app /etc/*paths.d/*XQuartz
sudo pkgutil --forget org.macosforge.xquartz.pkg
rm -rf ~/.serverauth*
rm -rf ~/.Xauthorit*
# rm -rf ~/.cache - bower 같은 프로그램과 관련된 파일을 삭제할 수 있음.
rm -rf ~/.rnd
rm -rf ~/Library/Caches/org.macosforge.xquartz.X11
rm -rf ~/Library/Logs/X11
모든 브라우저를 닫고, 다음 명령을 실행한다.
# 프로세스 검색 후 Kill
ps alwwx | grep -i evernote
kill 찾은 PID
sudo rm -rf /Applications/Evernote.app
rm -rf ~/Library/Application\ Support/Evernote
rm -rf ~/Library/Preferences/com.evernote.Evernote.plist
rm -rf ~/Library/Preferences/com.evernote.EvernoteThumbnailer.plist
rm -rf ~/Library/Preferences/com.evernote.SafariClipperPlugin.plist
sudo rm /usr/local/mysql
sudo rm -rf /usr/local/mysql*
sudo rm -rf /Library/StartupItems/MySQLCOM
sudo rm -rf /Library/PreferencePanes/My*
sudo rm -rf /Library/LaunchDaemons/com.microsoft.office.licensing.helper.plist
sudo rm -rf /private/var/db/receipts/*mysql*
rm -rf ~/Library/PreferencePanes/My*
sudo rm -rf /Library/Receipts/mysql*
sudo rm -rf /Library/Receipts/MySQL*
sudo rm -rf /var/db/receipts/com.mysql.*
sudo rm -rf "/Library/Internet Plug-Ins/Silverlight.plugin"
sudo rm -rf /Applications/Microsoft\ Silverlight
시스템 환경 설정 - 공유
다음 공유 서비스가 켜져 있었다.
모두 해제했다.
시스템 환경설정 > 사용자 및 그룹 > 로그인 항목
필요 없는 응용프로그램을 - 버튼을 사용해서 제거한다.
활성 상태 보기 앱을 실행해서 프로세스 이름으로 정렬해서 눈에 띄는 것이 있는지 확인한다. 여기서 delfino 같은 걸 찾았다. 메모리나 CPU 사용량으로 정렬해서 확인해보고, 높은 것이 있으면 위에 i 아이콘이 있는 버튼을 눌러 상세 정보를 보고 판단한다.
이제 블루투스 키보드, 마우스, 이어폰을 사용하지 않아서 모두 제거하고 블루투스 자체를 사용하지 않으므로 설정했다.
시스템 환경설정 > Bluetooth
Siri도 사용하지 않아서 껐다.
시스템 환경설정 > Siri
런치패드에서 물음표 생긴 아이콘을 휴지통으로 마우스로 드래그해서 이동하면 된다.
How do I get rid of this question mark shown in Launchpad?
Jekyll을 사용해서 글을 작성하는데, Generating 하는 시간이 너무 오래 걸려서 글 작성에 집중하기 어려웠다. 그래서 레이아웃이나 플러그인에 의해 Generating 하는 것 없이 순수하게 Markdown 변환만 하고 싶었다.
요구조건:
npm에서 markserv라는 적당한 모듈 찾았다.
사용 방법은 간단하다.
# global로 설치
npm install markserv -g
# 원하는 디렉터리로 이동
cd ~/workspace
markserv
이렇게 하면 자동으로 브라우저가 열린다. 그리고 수정할 때 Live reload가 된다.
이제 Jekyll은 사이트에 배포할 때만 빌드하면 될 것 같다.
브라우저는 내가 직접 띄우고, 포트도 8484번으로 사용할 것이라서 최종 커맨드를 다음과 같이 했다.
markserv -x -p 8484
PostgreSQL을 사용하면서 자주 사용하거나 알면 좋을 만한 내용을 정리했다.
내 PC에서 연결 가능한 서버는 pgAdmin을 쓰겠지만, 그렇지 않다면 서버에서 psql을 사용해야 한다.
접속은 다음과 같은 식으로 할 수 있다.
# 접속하기
psql -h 호스트 -p 포트 -U 사용자명 디비명
/* 모든 데이터베이스 보기 */
\list
/* or */
\l
/* 데이터베이스명만 보기 */
SELECT datname FROM pg_database;
/* 특정 조건에 속하는 데이터베이스만 보기 */
SELECT datname FROM pg_database WHERE datistemplate = false;
/* 스키마 불러오기 */
\dn
/* pg_catalog를 통해서 스키마 불러오기 */
select * from pg_catalog.pg_namespace;
/* public 테이블 보기 */
\dt
/* 또는 */
SELECT * FROM pg_catalog.pg_tables
/* 특정 스키마에 속하는 테이블 보기 */
\dt schema_name.*
/* |로 여러 스키마와 테이블을 필터링 */
\dt (public|schema_name).(a_table|b_table)
/* 모든 테이블 보기 */
SELECT table_schema,table_name FROM information_schema.tables ORDER BY table_schema,table_name;
/* DB 들어가기, 교체하기 */
\connect db_name
/* 컬럼 정보 보기 */
\d table_name
/* 나가기 */
\q
/* or */
CTRL + D
JMeter를 사용해서 사이트를 테스트하면서 작성한 내용이다.
Jmeter는 기본적으로 1회에 리다이렉트가 다섯 번 이상 발생하면, 다음과 같은 에러를 출력하면서 종료가 된다.
java.io.IOException: Exceeeded maximum number of redirects: 5
이것은 설정 파일을 변경해서 해결할 수 있다. 5회에서 100회로 바꿨다.
JMeter가 실행파일과 같은 디렉터리에서 httpclient.parameters:
...
# Maximum redirects to follow in a single sequence (default 5)
httpsampler.max_redirects=100
...
스레드의 한 주기에 Redirect가 여러 번 발생할 수 있는 테스트를 진행해야 했다. 여러 요청을 해야 하는 상황인데, 서버에서 쿠키를 생성해도 요청마다 새로운 세션으로 인식해서 테스트에 문제가 있었다. 즉, 이런 상태였다.
내가 원하는 것은:
다음과 같은 구조로 테스트 플랜을 구성해서 해결했다.
설정 | 의미 |
---|---|
Number of Threads (users) | 동시 요청하는 스레드 수 |
Ramp-Up Period (in seconds) | 상승 시간 (초 단위로). 한 주기가 어느 정도 간격을 두고 실행될지를 정하는 옵션. |
Loop Count | 얼마나 반복할지 |
Delay Thread creation until needed | 필요할 때까지 지연 스레드 |
Scheduler | 시작 시각, 종료 시각, 기간과 시작 시 지연을 설정할 수 있다. |
설정 | 의미 |
---|---|
Redirect Automatically | Redirect가 http 프로토콜스택에 의해 처리됨. |
Follow Redirects | Redirect가 JMeter에 의해 처리됨. |
다이어그램을 그리기 위해 PlantUML을 사용하고 있다. PlantUML 파일을 수정할 때, 실시간으로 그려진 다이어그램을 볼 수 있다는 장점 때문에 평소에 Atom 에디터를 사용했었다. Atom을 사용하는 이유는 이것밖에 없었다. 그래서 가능하면 이 작업도 서브라임텍스트로 할 수 있었으면 좋겠다고 생각했다.
Atom을 사용하기 전에 서브라임에서 PlantUML 문법 지원을 위해 플러그인을 하나 사용한 적이 있다. 문법 하이라이트는 잘 지원하지만, 내부적으로 PlantUML java 프로세스가 실행하고, 저장할 때마다 새로운 이미지 파일을 계속 생성하는 방식이었다. 이런 점 때문에, Atom 에디터로 넘어가게 됐었다.
“Node.js 서버가 PlantUML 파일을 이미지 파일로 제공하는 건 어떨까?”와 같은 생각을 했고, 그렇다면 에디터에서 파일을 수정하고, 브라우저로 확인하면 되므로, 파일이 생성되지 않아도 됐다.
그런데 기본 확장자가 wsd로 되어 있다. Atom에서는 puml을 사용해서 서브라임에서 이 파일들을 열면 Plain text로 나타난다. 확장자를 바꾸면 되지만, wsd보다는 puml이 PlantUML이라는 것을 더 잘 표현하는 것 같아서 puml을 그대로 사용하고 싶었다.
방법은 플러그인의 설정 파일만 변경하면 된다. 경로는 다음과 같다. 다른 플러그인도 비슷한 형태일 것으로 보인다.
# OSX
~/Library/Application Support/Sublime Text 3/Packages/sublime_diagram_plugin/Syntaxes/diagram.tmLanguage.xml
# Windows
C:\Users\{Username}\AppData\Roaming\Sublime Text 3\Packages\sublime_diagram_plugin\Syntaxes\diagram.tmLanguage.xml
puml이라고 되어 있는 부분을 추가하면 된다.
diagram.tmLanguage.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>fileTypes</key>
<array>
<string>wsd</string>
<string>puml</string> <!-- 추가!!! -->
</array>
<key>name</key>
<string>Diagram</string>
<key>patterns</key>
<array>
<dict>
<key>begin</key>
...
그리고 에디터를 더 빠르게 사용하기 위해서 서브라임텍스트에서 기본적이고, 필수적인 기능(Lint, Syntax highlight)을 제외하고 플러그인 사용하는 부분을 최대한 안 쓰려 노력하고 있다.
Bundler는 정확히 필요한 gem과 그 gem의 버전을 설치하고, 추적하는 것으로 일관성 있는 Ruby 프로젝트를 제공하는 도구이다. 기본적인 사용법을 알아본다.
설치방법 :
# bundler 설치하기
gem install bundler
이제 Gemfile을 프로젝트 루트 디렉터리에 만들어야 한다.
source 'https://rubygems.org'
gem 'nokogiri'
gem 'rack', '~> 2.0.1'
gem 'rspec'
다음 Bundler 커맨드를 사용할 수 있다.
# bundle을 설치한다.
bundle install
# bundle에 포함된 Gem 보기
bundle show
# bundle에 포함된 jekyll 버전 보기
bundle show jekyll
다양한 gem을 여기서 찾을 수 있다.
# 의존성 확인
gem dependency jekyll -v 2.5.3
All autocompleted
# gem 언인스톨
gem uninstall jekyll
프로젝트를 진행할 때 자바스크립트를 사용하는데, 오래된 프로젝트는 ES3, ES5를 사용하고, Webpack을 사용한 이후 프로젝트는 ES6만 사용한다. 프로젝트에 따라 문법을 다르게 써야 할 필요가 있는데, 서브라임에서는 기본적으로 한 확장자에 하나의 언어만 지원한다. 물론 변경할 수도 있지만 파일마다 변경해야 해서 귀찮다. 그래서 찾은 플러그인이 sublime-project-specific-syntax이다.
이 플러그인을 사용하면, 프로젝트마다 syntax를 설정할 수 있다.
방법은 플러그인 설치 후 프로젝트에서 다음과 같이 설정하면 된다.
Project - Edit Project
{
...
"syntax_override":
{
"\\.js|\\.jsx$":
[
"Babel",
"JavaScript (Babel)"
]
}
}
Gitlab을 관리하다가 발생했던 문제 해결 방법을 다룬다.
# 설정 반영
gitlab-ctl reconfigure
# 재시작
gitlab-ctl restart
# 설정 수정
vi /etc/gitlab/gitlab.rb
# gitlab 콘솔 실행
gitlab-rails console production
다음 ruby 코드를 입력한다.
u = User.where(id: 1).first
u.password = 'newpassword'
u.password_confirmation = 'newpassword'
u.save
관리자 페이지에서 실수로 signin_enabled를 false로 만들었을 때, 복원하는 방법은 다음과 같다.
s = ApplicationSetting.find_by(signin_enabled: false)
s.signin_enabled = true
s.save
Nginx Lua Module을 개발하면서 크롬에서는 쿠키가 저장되는데, IE11에서는 쿠키가 저장되지 않는 문제가 발생했다.
몇 가지 점검해야 할 부분이 있었다.
첫째, 서버 시간과 브라우저 시간이 일치하는지 확인해야 한다. 쿠키 삭제를 클라이언트 시스템 시간 기준으로 하니까 서버와 클라이언트와 시간 차이가 있는지 확인하는 것이 먼저다.
둘째, expires 필드의 날짜 포맷이 맞는지 확인한다.
정확한 포맷 :
Wed, 21 Oct 2015 07:28:00 GMT
비슷해 보였었는데, 약간 다른 부분을 확인했다.
expires = os.date("!%c", os.time() + 60) -- Thu Mar 23 01:10:04 2017 GMT
그래서 포맷을 정확하게 바꿨다.
-- Thu, 23 Mar 2017 01:15:36 GMT
expires = os.date("!%a, %e %h %Y %H:%M:%S GMT", os.time() + 60)
셋째, 도메인을 지정하지 않는 것이다. IE11에서는 도메인을 지정하지 말아야 한다.
넷째, max_age를 사용하지 말고, expires를 사용하는 것이 좋다. 오래된 IE에서 지원하지 않는다.
쿠키는 시간이 다른 문제를 교정하는 데 문제가 있어서 결국, 쿠키의 expires로 만료 시간을 지정하지 않고, nginx의 공유 메모리에 만료 시간을 지정하는 방식을 썼다.
PHP로 개발하면서 작성한 노트이다.
<?php
// 타입 확인
gettype($foo); // string
// 함수 존재 여부
function_exists("fopen"); // 1
<?php
// 파일로 로그 남기기
error_log("Error message.", 3, "/path/file");
message_type:
<?php
// 문자열이 배열 안에 있는지 여부
in_array("test", $arr)
// POST 방식으로 넘어온 별수를 p_ 접두어를 붙여 쓸 수 있다.
extract($_POST,EXTR_PREFIX_ALL,"p");
<?php
foreach($arr as $row) {
echo $row;
}
<?php
// 생성
setcookie("user", "Kichul", time() + 3600);
// 사용
echo $_COOKIE["user"]; // Kichul
// 제거
setcookie("user", "", time() - 3600);
<?php
$_SERVER['HTTP_USER_AGENT']; // Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
배열에서 중복된 값을 제거
<?php
array_unique(array("a", "a", "b")); // "a", "b"
배열의 키와 같은 이름의 변수로 만들어주는 함수.
<?php
$foo = array(
"bar" => "1",
"xar" => "2"
);
extract($db_config);
echo $bar; // 1
echo $xar; // 2
정규식 문자를 escape 한다.
<?php
$pattern_string = "??This string?? will be encoded.";
echo sprintf("/%s/", preg_quote($pattern_string)); // /\?\?This string\?\? will be encoded\./
<?php
// 파일 읽기
$handle = fopen("/home/php_script/test.text", "r");
if($handle) {
while (!feof($handle)) {
echo fgets($handle);
}
fclose($handle);
}
// 현재 커맨드를 실행하는 디렉터리
getcwd();
<?php
array_sum(explode(' ',microtime())) // 1490150613.7596
<?php
// 2017-04-12 11:13:54
date("Y-m-d H:i:s", time());
설정이나 Logger, DB를 다루는 클래스를 만들 때 자주 사용한다.
<?php
class SingletonClass
{
protected static $instance; // 현재 클래스의 인스턴스
private $config; // 멤버 변수
// 멤버 변수를 가져오는 메소드
public function get()
{
if ($this->config) {
return $this->config;
} else {
$initial_config = array(
"foo" => "1",
"bar" => "2"
);
$this->config = $initial_config;
return $initial_config;
}
}
// 싱글톤 인스턴스를 가져오는 메소드
final public static function getInstance()
{
if (!isset(static::$instance)) {
static::$instance = new static();
}
return static::$instance;
}
}
사용 방법:
<?php
$Config = Config::getInstance()->get();
echo $Config["foo"]; // 1
echo $Config["bar"]; // 2
<?php
# 접속
$pdo = new PDO(
'mysql:host=localhost;dbname=mydb',
'user',
'passwd'
);
# 조회
$sth = $pdo->prepare("select * from images");
$sth->execute();
$result = $sth->fetch(PDO::FETCH_ASSOC);
로그를 남기기 위해 기본적으로 Monolog를 다음과 같이 사용할 수 있다.
<?php
// Alias 지정
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// Handler 생성 (표준 출력)
$handler = new StreamHandler('php://stdout');
// Logger 생성
$logger = new Logger('ApplicationLog');
// Logger에 Handler 넣기
$logger->pushHandler($handler);
// 로그 기록하기
$logger->info(LOG_TEXT)
이번에는 Monolog로 전역에서 사용할 수 있는 Logger를 만들겠다. 매뉴얼에 따르면 Registry라는 싱글톤을 사용해서 어디에서든 호출할 수 있다.
<?php
// Alias 지정
use Monolog\Registry;
// ... logger 선언은 위 내용과 같음 ...
// Logger를 전역에서 사용할 수 있도록 추가
Registry::addLogger($logger);
// 어디서든 다음과 같이 사용할 수 있다.
Registry::getInstance("ApplicationLog")->info(LOG_TEXT);
지금까지 StreamHandler로 예제를 만들었는데, 다음과 같은 핸들러를 사용할 수 있다.
<?php
// 표준 입출력, 파일로 보내는 Handler
use Monolog\Handler\StreamHandler;
// 아무런 출력도 생기지 않음.
use Monolog\Handler\NullHandler;
// 시스템 로그 파일에 기록 (centos : /var/log/message)
use Monolog\Handler\SyslogHandler;
// PHP 에러로그 파일에 기록
use Monolog\Handler\ErrorLogHandler;
Formatter를 설정하지 않으면 기본적인 포맷으로 로그가 기록된다. 이것을 원하는 형식으로 지정하려면 Handler에 Formatter를 설정해야 한다.
<?php
// Alias 지정
use Monolog\Formatter\LineFormatter;
// 새로운 형식으로 formatter 생성
$formatter = new LineFormatter("%message%\n");
// handler에서 formatter를 지정할 수 있다.
$handler->setFormatter($formatter);
아파치는 다음과 같은 형태로 로그 파일이 생성된다.
/var/log/httpd/호스트명-access_log
/var/log/httpd/호스트명-error_log
셸에서 실행할 때는 ini 파일에서 로그 파일의 위치를 알아낼 수 있다.
# 어떤 php.ini 파일을 불러오는지 확인
php --ini
Loaded Configuration File: /etc/php.ini
# error_log 설정 확인
cat /etc/php.ini | grep error_log
error_log = /home/test/php_errors.log
<?php
print_r($argv);
특정 디렉터리에 있는 커맨드만 실행할 수 있게 제한할 수 있다.
; 안전모드 실행여부
safe_mode = On
; exec 함수에서 허용할 디렉터리를 설정
safe_mode_exec_dir = "/path/bin"
보안에 위협이 될만한 함수를 제한할 수 있다.
; 특정 함수 사용 제한하기
disable_functions = "passthru,proc_open,pcntl_exec,shell_exec,system,eval,popen"
phar 파일을 만들 때, 파일을 압축하는 부분에서 다음 에러가 발생했다.
unable to create temporary file
open file descriptor의 수를 확인하고, 너무 작으면 올려준다.
# 확인
ulimit -n
1024
# 변경
ulimit -n 20000
위 커맨드는 일시적으로 적용하는 것이라서 설정을 유지하려면 다음과 같이 설정 파일을 이용하거나 profile 파일에 추가해야 한다.
# 프로세스 사용자에 대한 설정
vi /etc/security/limits.conf
# profile에 설정. 가장 아래에 ulimit -n 20000 추가
vi /etc/profile
# 적용
source /etc/profile
자주 사용하는 GIT 커맨드와 팁을 정리했다.
# 원격 저장소 소스 가져오기
git clone https://domain/project.git
# git 버전관리에 추가하기
git add 파일명
# 버전 관리에서 제외
git rm --cached 파일명
# 변경된 파일 최신 커밋된 파일로 되돌리기.
git checkout -- 파일명
# 특정 해시코드로 파일 가져오기
git show ce425c1f4fad4f370b9c88510c36d5e26c847f7c:./README.md > ./README.md
# svn의 export 처럼 zip 파일로 저장
git archive --format zip --output /full/path/to/zipfile.zip master
# push 할 때 기본 업스트림 지정하기
git push --set-upstream myrepo master
# 잘못 등록된 식별정보 수정
git commit --amend --author='Your Name'
# 디렉터리 보기
git ls-tree --full-tree -r HEAD
git ls-tree HEAD ./src
# 설정 리스트 보기
git config --list
# 태깅. 모든 버전보기
git tag -l
git tag -l v*
# 태깅하기
git tag v1.0.0
# 원격 저장소의 태그 지우기
git push --delete origin v1.5.0
# 태그 포함해서 푸시
git push --tags
# 태그이름으로 체크아웃
git checkout v2.0.0
# 현재 어떤 태그를 선택했는지 보기
git describe --tags
# 현재 브랜치 상태 보기
git branch
# 브랜치 상태 보기
git remote show origin
# 원격의 브랜치 보기
git branch -r
# 원격 브랜치로부터 가져오기
git checkout -b 생성할브랜치이름 원격브랜치이름
# 원격 브랜치로부터 가져오기(브랜치이름 그대로 사용할 경우)
git checkout -t origin/원격브랜치이름
# 브랜치 삭제
git branch -d v1.0.0
# 브랜치의 업스트림 변경하기 anoter_origin -> origin
git branch -u origin/master
Branch master set up to track remote branch master from origin.
# 원격 저장소 모두 보기
git remote show
# 원격 저장소 추가하기
git remote add origin http://git-server-domain/git-path.git
# 원격 저장소 이름 바꾸기
git remote rename origin redmine
# 특정 원격 저장소 보기
git remote show origin
# 원격 저장소 제거
git remote remove origin
# 모든 원격 저장소에 푸시하기
# 파일에 관한 로그만 표시
git log --name-only
# 최근 로그 2개만 보기
git log -p -2
# diff 보기
git log -p 파일명
# 삭제된 파일 diff를 파일로 출력
git log -p -- 삭제된파일명 > a.txt
# 모든 브랜치 로그 보기
git log --all
git config --global user.name "Your Name"
git config --global user.email you@example.com
git reset -- ./src
bower에서 git 프로토콜을 사용하면 잘 안되는 경우가 있는데, https로 바꿨더니 잘 동작했다.
git config --global url."https://".insteadOf git://
.gitignore 파일에 제외할 파일, 디렉터리 목록을 작성한다. 필요한 디렉터리에 이 파일을 넣어두면 된다.
다음은 예제다.
node_modules
bower_components
.DS_Store
전역으로 설정하는 방법:
git config --global core.excludesfile ~/.gitignore_global
이렇게 설정하고, .gitignore_global 파일도 .gitignore와 동일하게 작성한다.
윈도에서 파일에 Unix 파일로 저장되었을 때 다음과 같은 메시지가 발생한다.
LF will be replaced by CRLF in git - What is that and is it important? [duplicate]
http://stackoverflow.com/questions/5834014/lf-will-be-replaced-by-crlf-in-git-what-is-that-and-is-it-important
다음 명령을 통해 이 메시지를 없앨 수 있다.
git config core.autocrlf false
git config --global core.autocrlf false
다음 메시지가 발생하는 경우가 있다.
push.default is unset WARNING
다음 명령으로 해결할 수 있다.
git config --global push.default simple
# dropbox에 GIT 프로젝트들이 위치할 디렉터리 생성
mkdir /dropbox/gitrepo
cd /dropbox/gitrepo
# 프로젝트 디렉터리 생성
mkdir project.git
cd project.git
git init --bare
# 작업 디렉터리로 가서 저장소와 연결
cd /workspace/project
git remote add dropbox file:///dropbox/gitrepo/project.git
git push dropbox master
여러 버전의 git이 설치되어 있어서 하나만 남기고 지우니 다음과 같은 에러가 발생했다.
λ git push
fatal: unable to access 'https://user_id@domain/project.git/': error setting certificate verify locations:
CAfile: C:/Program Files (x86)/Git/mingw32/ssl/certs/ca-bundle.crt
CApath: none
먼저 ca-bundle.crt 파일이 있는 경로를 찾아낸다. 그리고 관리자 권한으로 커맨드 창을 열고 다음을 실행한다.
git config --system http.sslcainfo "C:\Program Files\Git\mingw64\ssl\certs\ca-bundle.crt"
만약 관리자 권한이 없다면 다음 에러가 발생한다.
error: could not lock config file c:\Program Files\Git\mingw64/etc/gitconfig: Permission denied
리눅스 계열 OS를 사용하면서 자주 사용하는 명령어를 정리했다. Centos를 주로 사용해서 다른 OS에서 안 될 수 있다.
if [ -f "/path/filename" ]
then
echo "file 있음"
else
echo "file 없음"
fi
userdel 삭제할 아이디
# CPU로 정렬
ps -o pid,ucmd,uid,ucomm,comm,command,args ax --sort pcpu
ps -o pid,pcpu,args ax --sort pcpu
# nginx 프로세스 Kill
ps aux | grep nginx | awk '{system("kill "$2)}'
ps aux | grep nginx | awk '{print $2}'
lsof | grep 파일명
sed -n 28853p < test.txt
cat 말고 다른 커맨드에서도 응용할 수 있다.
cat << EOF
hello world
EOF
특정 파티션을 포맷하고, 마운트하는 명령이다.
# ext4로 포맷
mkfs.ext4 /dev/vda
# 마운트하기
mount /dev/vda /data_dir
# nginx.conf가 포함되어 있는 모든 위치를 출력
locate nginx.conf
# 특정 내용을 포함하는 파일 찾기
find ./ -name "*" | xargs grep "내용"
# rsync로 최근 7주일간 변경된 파일 동기화
find . -type f -mtime -7 -print | rsync -av --files-from=- . /dest
# 현재 디렉터리에서 5분 지난 파일 찾아서 지우기
find . -type f -cmin +5 -exec rm -f {} \;
# 특정 디렉터리 제외하고 복사하기
find SOURCE_PATH \
-type f \
-not -path '*EXCLUDE_PATH1*' \
-not -path '*EXCLUDE_PATH2*' \
-exec \
cp --parents '{}' TARGET_PATH \;
/sbin/ifconfig -a
cat /etc/resolv.conf
/sbin/route -n
/sbin/arp -n
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
타임서버를 사용해서 OS의 시간을 설정한다.
# 시간 맞추기
rdate -s time.bora.net
# 현재 호스트명 확인
hostname
# 호스트명 변경하기
vi /etc/sysconfig/network
# 네트워크 서비스 재시작
service network restart
ngrep port 80
# 1초마다 test.json 파일 내용 출력
watch --interval 1 cat test.json
CPU 관련:
# CPU 속도
dmesg | grep processor
# CPU 갯수
grep -c processor /proc/cpuinfo
OS에서 사용하는 bit를 확인할 수 있다.
arch
메모리 관련:
cat /proc/meminfo
하드디스크 관련:
# scsi
cat /proc/scsi/scsi
# ide
cat /proc/ide/hda/model
# raid
cat /proc/mdstat
네트워크 관련:
cat /proc/net/netlink
이 커맨드를 사용하려면 Apache 설치가 요구된다.
# 10개의 스레드로 4번 요청. 총 요청수 : 40번
ab -n 4 -c 10 http://domain/filename.php
-v 옵션을 사용한다.
# grep을 제외
ps aux | grep perl | grep -v grep
# watch 예제. watch에서 파이프를 쓰고 싶을 때 '를 사용
watch 'ps aux | grep nginx | grep -v grep | grep -v tail | grep -v watch'
top -p 23583
윈도 텍스트파일에 있는 CR(캐리지 리턴)을 제거하는 방법이다.
sed -i -e 's/\r$//' test.sh
# GET 방식
curl -XGET http://domain/filename.php
# POST 방식
curl -X POST http://domain/filename.php \
-H "Content-Type:application/json" \
-d '{\"key\": \"value\", \"key2\": \"value2\"}'
wc를 사용해서 크기를 잴 수 있다.
# 크기 재는 방법
curl -XGET https://server/path | wc -c
# 압축한 크기
curl -XGET https://server/path --compress | wc -c
더 나은 방법으로 w 옵션을 사용할 수 있다.
# 크기 재는 방법
curl -so /dev/null https://server/path -w '%{size_download}'
# 압축한 크기
curl --compressed -so /dev/null https://server/path -w '%{size_download}'
# gzip 하는 방법
gzip -c -f << EOF
uncompress data
EOF
Is there a way to input automatically when running a shell?
test.sh <<!
y
pasword
!
셸에서 다중 화면을 사용할 수 있는 screen에서 사용할 수 있는 단축키.
su -s /bin/bash jenkins
cat /etc/issue
CentOS release 5.5 (Final)
Kernel \r on an \m
# 다양한 정보 출력
grep . /etc/*-release
date +%s
1489857490
# home 디렉터리로 이동 후 파일목록 출력
cd /home && ls -al
# 컴파일부터 설치까지
./configure ; make ; make install
man -P cat dig
# 원격 호스트에 있는 파일 로컬의 현재 디렉터리로 복사
scp root@hostname:/usr/local/lua/lib/cjson.so .
# 메모리 파싱
free -m | awk '/Mem:/ { print $2 } /buffers\/cache/ { print $3 }'
# rpm.spec에서 버전값만 가져오기
cat rpm.spec | grep Version | awk '{ print $2 }'
# Makefile에서 사용할 때
VERSION = `cat rpm.spec | grep Version | awk '{ print $$2 }'`
echo VERSION
Ubuntu에서 기본 에디터가 vi가 아니라서 다음과 같은 방법으로 에디터를 바꿨다.
vi ~/.bashrc
export EDITOR=/usr/bin/vim
source ~/.bashrc
.vimrc :
set enc=UTF-8
set fenc=UTF-8
set tenc=UTF-8
.vimrc :
syntax on
터미널에서 내용을 붙여넣기를 할 때 밀리는 경우가 있는데, 설정을 통해 해결할 수 있다.
.vimrc :
set pasete
set nopaste
Memcached는 Telnet으로 접속해서 명령을 실행할 수 있다.
# 접속
telnet localhost 11211
# 데이터 가져오기
get key_name
# 데이터 저장하기
set key_name 0 6000 24
value...
# 상태 보기
stats
STAT pid 2121
...
# 아이템 모두 보기
stats items
STAT items:2:number 811
STAT items:2:age 8139
STAT items:2:evicted 0
STAT items:2:evicted_nonzero 0
STAT items:2:evicted_time 0
STAT items:2:outofmemory 0
STAT items:2:tailrepairs 0
STAT items:2:reclaimed 3997298
STAT items:2:expired_unfetched 3997297
STAT items:2:evicted_unfetched 0
...
# 위에서 얻은 번호로 확인 (slab id, 사이즈)
stats cachedump 2 1
ITEM 8c12370438d86a5b4507c2be836ee538 [0 b; 1489460795 s]
그동안 MySQL을 사용하면서 자주 사용했던 질의를 정리했다.
/* 프로세스 보기 */
SHOW PROCESSLIST
/* 좀 더 자세히 프로세스 보기 */
SHOW FULL PROCESSLIST
/* 서버 상태 보기 */
SHOW STATUS
/* 현재 세션 변수들 */
SHOW SESSION VARIABLES
/* Global 변수들 */
SHOW GLOBAL VARIABLES
/* 프로세스 Kill */
kill 프로세스ID
/* 열린 테이블 확인 */
SHOW OPEN TABLES FROM dbname\G
/* Read Lock */ lock tables table_name READ;
/* Write Lock */ lock tables table_name WRITE;
/* 여러 개도 가능 */ lock tables table_name WRITE, table_name2 READ;
/* 락 걸려있을 때 해제 */ UNLOCK TABLES;
/* Global Lock */ FLUSH TABLES WITH READ LOCK;
/* Innodb Dead Lock 확인 */ SHOW innodb status show engine innodb status
SET GLOBAL concurrent_insert = 'AUTO';
SET SESSION concurrent_insert = 'AUTO';
concurrent_insert = AUTO
concurrent_insert = "AUTO"
/* 이미 존재하는 테이블에 LIST 형태로 파티션 등록 */
ALTER table `tablename` PARTITION BY LIST (dt)
(
PARTITION p20140409 VALUES IN (20140409),
PARTITION p20140410 VALUES IN (20140410)
)
/* 이미 존재하는 테이블에 RANGE 형태로 파티션 등록(datetime 필드를 사용하는 경우) */
ALTER TABLE `tablename` PARTITION BY RANGE (UNIX_TIMESTAMP(stamp_inserted))
(
PARTITION p2014082620 VALUES LESS THAN (1365990900),
PARTITION p2014082621 VALUES LESS THAN (1365991801)
);
/* 이미 파티션이 정의된 테이블에 파티션 추가 */
ALTER TABLE `tablename` ADD PARTITION (
PARTITION p20140423 VALUES IN (20140423)
);
/* 파티션 존재 여부 확인 */
SELECT * FROM information_schema.partitions WHERE table_name='tablename'
/* 파티션 삭제 */
ALTER TABLE tablename DROP PARTITION partition_name;
/* 파티션 재배치. p201410 파티션이 있는 상태에서, 분리한다. */
ALTER TABLE `tablename` REORGANIZE PARTITION p201410 INTO (
PARTITION p201409 VALUES LESS THAN (5415777),
PARTITION p201410 VALUES LESS THAN MAXVALUE
);
CREATE USER 'root'@'127.0.0.1' IDENTIFIED BY 'password';
grant all privileges on *.* to root@127.0.0.1 with grant option;
/* View만 보기 */
show full tables where Table_type="VIEW";
/* 트리거 보기 (use dbname 이후) */
show triggers
/* table_a에 INSERT될 때 table_b에 INSERT하는 트리거 등록 */
DELIMITER $$
CREATE TRIGGER `dbname`.`table_a_AFTER_INSERT` AFTER INSERT ON `table_a` FOR EACH ROW
BEGIN
INSERT INTO `dbname`.`table_b` set id = NEW.id;
END$$
DELIMITER ;
/* table_a가 삭제될 때 table_b의 레코드를 삭제하는 트리거 등록 */
DELIMITER $$
CREATE
TRIGGER `dbname`.`table_a_AFTER_DELETE` AFTER DELETE
ON `dbname`.`table_a`
FOR EACH ROW BEGIN
DELETE FROM `dbname`.`table_b` WHERE id = old.id;
END$$
DELIMITER ;
이벤트가 활성화되었는지 확인하고, 활성화하는 방법이다.
/* 확인 방법 */
SHOW VARIABLES LIKE "%event%";
/* Event 활성화 */
SET GLOBAL event_scheduler = ON;
SET @@global.event_scheduler = ON;
SET GLOBAL event_scheduler = 1;
SET @@global.event_scheduler = 1;
위 방법은 임시적인 방법이라서 설정 파일을 수정해야 한다.
my.ini:
[mysqld]
event_scheduler=on
이벤트를 생성해서 주기적으로 질의나 프로시저를 실행할 수 있다.
DELIMITER $$
CREATE EVENT `dbname`.`eventname`
ON SCHEDULE
EVERY 1 MONTH
STARTS CURRENT_TIMESTAMP
DO
BEGIN
CALL procedurname();
END$$
DELIMITER ;
/* 버전 확인 */
SHOW VARIABLES LIKE "%version%";
/* KST를 UTC로 변경 */
SELECT CONVERT_TZ('2014-09-25 09:49:31', '+09:00','+00:00')
/* 최대 사용가능한 연결수 확인 */
SHOW STATUS WHERE variable_name='Max_used_connections';
/* 사용자 추가 */
CREATE USER 'root'@'127.0.0.1' IDENTIFIED BY '비밀번호';
GRANT ALL PRIVILEGES ON *.* TO root@127.0.0.1 WITH GRANT OPTION;
/* 사용자 비밀번호 변경 */
SET PASSWORD FOR root@localhost=PASSWORD('새비밀번호');
FLUSH PRIVILEGES;
/* 깨진 테이블 복구 */
repair table `tablename`;
/* 파일로부터 데이터 불러오는 명령 */
LOAD DATA INFILE 'a.csv' INTO TABLE `tablename`
FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\';
/* table의 모든 컬럼 보여주기 */
SHOW FULL COLUMNS FROM `tablename`
/* 특정일자 이전인 경우 삭제 */
DELETE FROM `tablename` WHERE date_field < '2013-01-01 00:00:00'
SHOW VARIABLES LIKE 'have_innodb';
disabled일 경우 설정 파일을 변경해서 InnoDB를 활성화할 수 있다.
my.cnf
# 주석 처리로 InnoDB를 활성화
# skip-innodb
/* 특정 날짜 이전 바이너리 로그 제거 */
PURGE BINARY LOGS BEFORE '2014-07-15 00:00:00';
설정 파일에서 보관기간을 지정할 수 있다.
my.cnf:
expire_logs_days = 7
Nginx Lua Module을 개발하면서 필요한 내용을 정리했다.
Lua nil은 값이 없다는 것을 의미한다.
if keys ~= nil then
ngx.log(ngx.INFO, "nil이 아니다")
end
if keys == nil then
ngx.log(ngx.INFO, "nil이다")
end
tonumber("10") -- 10
tostring(10) -- "10"
공유 메모리의 키를 가져오는데, 어떤 형태인지 알아보려고 사용했었다.
-- 출력 : table
ngx.log(ngx.INFO, type(ngx.shared.shared_dict:get_keys()))
공유메모리의 키를 모두 가져와서 For Loop을 돌릴 수 있다.
-- k : 인덱스, v : 키 문자열
for k, v in pairs(ngx.shared.shared_dict:get_keys()) do
ngx.log(ngx.INFO, v) -- 각 키값들이 출력됨.
end
string.len("abcdefg") -- 7
-- sprintf처럼 포맷 지정
string.format('%s/AAA', 'BBB') -- BBB/AAA
-- 문자열 찾기
local str = "This is a string."
if string.match(str, "This") then
-- 찾음
end
-- 문자열 치환
string.gsub(ngx.var.request_uri, "?.*", "") -- ?부터 제거
os.time() -- 1490167184
os.date("%c", os.time()) -- Wed Mar 22 16:20:06 2017 (KST 또는 시스템 설정에 따름)
os.date("!%c", os.time()) -- Wed Mar 22 07:20:47 2017 (UTC)
-- 카운팅
table.getn(ngx.shared.shared_dict)
Nginx 로그파일에 로그를 단계별로 기록할 수 있다.
ngx.log(ngx.INFO, "Log")
ngx.log(ngx.DEBUG, "Log")
ngx.log(ngx.WARN, "Log")
ngx.log(ngx.ERR, "Log")
모듈화를 위해서 다음과 같은 형태로 파일을 만들어서 사용할 수 있다.
-- mymodule.lua
local _M = {}
function _M.main(ctx)
ctx.foo = "bar"
end
return _M
이렇게 작성한 모듈은 다음과 같이 사용할 수 있다.
local Mymodule = require 'mymodule';
Mymodule.main({foo: "foo"})
script_path()를 호출하는 파일의 경로를 구할 수 있다. 해당 파일을 기준으로 다른 파일 경로를 가져올 때 유용하다.
function script_path()
local str = debug.getinfo(2, "S").source:sub(2)
return str:match("(.*/)")
end
script_path() -- /home/luascript/access.lua
local pcall = pcall
local ok, upstream = pcall(require, "ngx.upstream")
if not ok then
error("ngx_upstream_lua module required")
end
Nginx에 LUA 코드를 끼워 넣어 사용할 수 있는 모듈로 기본적으로 Nginx에서 제공되지 않고, 직접 설치해야 한다.
원하는 단계에 넣으면 중간에 다른 결과를 출력할 수 있다.
ngx.header.content_type = 'text/html';
ngx.print("hello")
ngx.exit(200)
ngx.config.nginx_version -- nginx 버전
ngx.config.ngx_lua_version -- nginx lua 버전 9015
ngx.location.capture는 nginx 워커에서 특정 URI에 추가 요청을 할 수 있다.
local res = ngx.location.capture("/auth")
if res then
ngx.say("status: ", res.status)
ngx.say("body:")
ngx.print(res.body)
end
특정 페이지로 리다이렉트할 수 있다.
-- redirect
ngx.redirect("http://"..ngx.req.get_headers()["Host"].."/page")
-- exec
ngx.exec("@bar", "a=goodbye");
exec는 redirect와 다르게, 내부 리다이렉트를 하고, 새로운 외부 HTTP 트래픽과 관련이 없다.
-- upstream으로 전달
ngx.req.set_header("X-Epoch", ngx.time())
-- 응답 헤더에 추가한다. phase간 값을 공유할 수도 있다. 예 : access phse -> header_filter phase
ngx.header['X-Epoch'] = ngx.time()
ngx.header['Content-Type'] = "text/html"
ngx.header['Set-Cookie'] = 'Foo=abc; path=/'
nginx.conf:
map "${http_host}${request_uri}" $value_in_lua {
default "default_value";
# 정규식 사용 가능. ~ : case-sensitive, ~* : case-insensitive
~*^(.+\.)?hostname/path "value";
}
주소가 설정한 값과 일치하면 그 값을 LUA에서 다음과 같이 사용할 수 있다.
ngx.var.value_in_lua -- value
LIP는 LUA INI Parser다. ini 파일을 불러오고, 저장할 수 있다.
-- ini 불러오기
local LIP = require 'LIP';
local config = LIP.load('/home/luascript/config.ini');
CJSON은 LUA에서 JSON을 지원하는 모듈이다.
다음 페이지 다른 JSON 모듈을 비교한다.
LUA에서 다음 에러가 발생한다.
a temporary file /path/file while reading upstream, client: 000.000.000.000, server: localhost, request: "GET /file.php HTTP/1.1", upstream: "http://127.0.0.1:10000/file.php", host: "domain"
문제 생기는 부분을 확인하니 다음과 같이 이미지를 base64로 출력하는 부분의 크기가 설정한 값보다 커서 생긴 문제였다. 이미지 크기에 맞게 프록시 버퍼의 크기를 늘려줬다.
<?php
<img src='data:image/jpeg;base64, <?php echo $image?>' width="100%">
Nginx 설정을 변경했다.
proxy_buffers 500 8k;
예제 프로그램이라서 base64로 이미지를 출력했지만, 버퍼를 늘리는 것보다 이미지는 static 서버를 통해 제공하는 것이 좋다.
CSS로 말 줄임 처리하는 방법이다. 여기서 width는 반드시 지정해야 한다.
.truncate {
width: 250px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
테이블에서는 다음과 같이 한다.
HTML:
<table>
<tbody>
<tr>
<td>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</td>
<td>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</td>
</tr>
</tbody>
</table>
CSS:
table {
table-layout: fixed
}
td {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
Jekyll에서 관련 포스트 기능이 이상하게 동작해서 해당 플러그인을 수정할 필요가 있었다. 플러그인이 Ruby라서 관련 문법을 정리해서 다음에 수정할 때 참고해야겠다.
디버깅 관련 :
puts variable.inspect
puts variable.methods.sort
puts variable.to_yaml
문자열 관련 :
# concat
STR1 = "A"
STR2 = "B"
source = "#{STR1}/#{STR2}/App.config"
# replace
'AAA BBB'.gsub('AAA', 'CCC')
배열 관련 :
# 배열 생성
arr = []
# 배열에 원소 추가
arr << el
# 배열 갯수
arr.length
# 배열 루프
arr.each do |row|
puts "row: #{row}"
end
# mixed list
site.pages.each {|row| puts "#{row.title}"}
# 배열 모두 제거. arr을 전체 원소에 remove 액션 실행
arr.map(&:remove)
클래스 관련 :
# 클래스 선언
class ClassName
end
# 새로운 인스턴스 만들기
instance = ClassName.new
# 모듈 - ModuleName.rb로 저장
module ModuleName
def ModuleName.methodName()
return 'return value'
end
end
# 모듈 사용 - return value를 출력
require './ModuleName.rb'
puts ModuleName.methodName()
모듈 확장하는 방법:
# 모듈 정의
module ModuleName
def ModuleMethodName
puts "return value"
end
end
# 인스턴스 메소드로 사용 가능
class ClassName1
include ModuleName
end
ClassName1.new.ModuleMethodName # return value
# 클래스 메소드로 사용 가능
class ClassName2
extend ModuleName
end
ClassName2.ModuleMethodName # return value
self.included를 사용한 모듈 확장
# 확장 모듈
module ModuleName
# ClassModule을 클래스 메소드로 사용하기 위해 확장하는 역할
def self.included(klass)
klass.extend(ClassModule)
end
# 클래스 메소드 정의 모듈 정의
module ClassModule
def ClassMethodName
return "return class"
end
end
# 인스턴스 메소드
def InstanceMethodName
puts "return instance"
end
end
# ModuleName을 확장한 클래스 정의
class ClassName
include ModuleName
end
# 클래스 사용
ClassName.ClassMethodName # return class
ClassName.new.InstanceMethodName # return instance
gem을 설치하는데, 다음 에러가 발생했다.
λ gem install mdl
ERROR: Could not find a valid gem 'mdl' (>= 0), here is why:
Unable to download data from https://rubygems.org/ - SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (https://api.rubygems.org/specs.4.8.gz)
SHA-1 개인 키 서명이 취약해지면서 브라우저가 변하는 것을 대비하려고, 웹서버들이 SHA-2(특히, SHA256 이상)로 업그레이드하게 된 것이 원인이다.
해결 방법은 최신 RubyGems을 내려받고 설치하는 것이다.
gem install rubygems-update-2.6.11.gem
update_rubygems
포스팅을 작성할 때마다 새로운 파일을 생성하고, 이름을 적고, 메타 정보를 입력하는 것이 번거로웠다. 그래서 스크립트로 간단하게 이 작업을 자동화할 수 있는 것을 만들려고 했다. Python으로 만들지 Ruby로 만들지 고민하던 중 sublime-jekyll이란 걸 보게 됐다.
이거였다.
설치하기:
패키지 컨트롤에서 Jekyll을 검색해서 설치
설치한 후 바로 동작하지 않고, 프로젝트 설정이 필요하다. 직접 post, draft와 같은 디렉터리를 지정할 수도 있지만, 자동으로 찾는 옵션인 jekyll_auto_find_paths를 true 설정했고, 확장자도 기본이 markdown이라서 md로 변경했다.
{
"folders": [
....
],
"settings":
{
"Jekyll":
{
"jekyll_auto_find_paths": true,
"jekyll_markdown_extension": "md",
"jekyll_datetime_format": "%Y-%m-%d %H:%M:%S+0900"
}
}
}
더 자세한 설정은 여기서 확인할 수 있다.
그리고 템플릿을 만들어서 새로운 포스트를 생성할 수 있다. 그래서 템플릿을 다음과 같이 만들었다.
---
layout: post
title: ""
description: ""
permalink: /
date: +0900
categories:
-
tags:
-
---
...
괜찮은 기능
jekyll_auto_find_paths를 true로 설정했었는데, 이것 때문에 키보드로 입력했을 때 도저히 사용 못 할 정도로 아주 느려지는 문제가 발생했다.
수동으로 경로 설정을 해야 하고, 반드시 절대 경로를 사용해야 한다.
{
...
"settings":
{
"Jekyll":
{
"jekyll_posts_path": "/Users/kichuljung/workspace/kichul_blog/_posts",
"jekyll_drafts_path": "/Users/kichuljung/workspace/kichul_blog/_drafts",
"jekyll_markdown_extension": "md"
}
}
...
}
서브라임텍스트에서 프로젝트를 검색할 때 node_modules 같은 디렉터리에 있는 파일이 검색되는 경우가 많아서 불편했다. 그래서 새로운 프로젝트를 만들거나 장비가 바뀌었을 때마다 구글링하던 내용을 포스팅했다.
메뉴 또는 커맨드 팔레트에서 프로젝트 수정 명령을 실행한다.
Project: Edit
에디터에 JSON 파일이 나타나면 folder_exclude_patterns을 추가하고, 제외할 디렉터리와 파일을 입력하면 된다. 아래 예제는 Jekyll에서 SASS 캐시와 HTML로 생성된 결과를 제외한 경우다.
{
"folders":
[
{
"path": "/Users/jungkichul/workspace/kichul_blog",
"folder_exclude_patterns":
[
".sass-cache", "_site"
]
}
]
}
ssh는 등록된 키로 인증해서 비밀번호를 입력할 필요가 없었는데, 내가 사용하는 git 서버가 http만 지원하면서 문제가 생겼다. 비밀번호를 매번 입력하는 것이 불편해서 방법을 찾게 되었다.
git config --global credential.helper cache
윈도에서는 안 된다. 아래 페이지에서 내려받고, winstore를 설치한다.
터미널을 다시 실행한 후 아래 커맨드를 실행한다.
git config --global credential.helper winstore
이후 push, pull과 같이 인증이 필요한 커맨드를 실행했을 때, 비밀번호 입력창이 뜨고, 비밀번호를 입력할 수 있다. 그 이후에는 비밀번호를 더 이상 요구하지 않는다.
원격 저장소와 관련된 커맨드를 정리했다.
# 원격 저장소 모두 보기
git remote show
gitlab
origin
# 원격 저장소 이름 바꾸기
git remote rename origin redmine
git remote rename gitlab origin
# 특정 원격 저장소 보기
git remote show origin
* remote origin
Fetch URL: http://host.domain/kcjung02/project-name.git
Push URL: http://host.domain/kcjung02/project-name.git
HEAD branch: master
Remote branch:
master tracked
Local ref configured for 'git push':
master pushes to master (fast-forwardable)
R로 트래픽과 관련된 시뮬레이션 작업을 하면서 필요했던 부분에 대해 기록했다.
1+1 # 2
1-1 # 0
1*2 # 2
2/1 # 2
# 나머지
3%%2 # 1
R에서는 is를 사용해서 타입을 체크할 수 있다.
foo = NA
bar = 'bar'
if(is.na(foo)) {
print('foo is null.')
}
if(!is.na(bar)) {
print('bar is not null.')
}
C에서 printf와 유사하게 R에서도 포맷에 맞춰 출력할 수 있다.
A = 'A'
B = 'B'
C = 'C'
print(
sprintf(
"OUTPUT : %s, %s, %s", A, B, C
)
)
[1] "OUTPUT : A, B, C"
어떤 변수의 타입을 알아낼 때 필요한 함수들이다.
# class
class(mtcars)
[1] "data.frame"
# str
str(mtcars)
'data.frame': 32 obs. of 11 variables:
$ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
$ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
$ disp: num 160 160 108 258 360 ...
$ hp : num 110 110 93 110 175 105 245 62 95 123 ...
$ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
$ wt : num 2.62 2.88 2.32 3.21 3.44 ...
$ qsec: num 16.5 17 18.6 19.4 17 ...
$ vs : num 0 0 1 1 0 1 0 1 1 1 ...
$ am : num 1 1 1 0 0 0 0 0 0 0 ...
$ gear: num 4 4 4 3 3 3 3 4 4 4 ...
$ carb: num 4 4 1 1 2 1 4 2 2 4 ...
idx = match(key, temp_list$key)
# for 루프
for(mpg in mtcars$mpg) {
print(mpg)
}
# while 루프
i = 1
total = length(mtcars$mpg)
while(TRUE) {
if(total < i) {
break
}
print(mtcars$mpg[i])
i = i + 1
}
a = c("A", "B", "C", "D", "E")
b = c("1", "2", "3", "4", "E")
intersect(a, b) # E
PHP의 __FILE__와 같이 소스 파일의 경로를 R에서도 구할 수 있다.
filepath = (function() {
return(attr(
body(
sys.function()
), "srcfile"
)$filename)
})()
print(filepath) # c:/temp/test.txt
# 디렉터리도 구할 수 있다.
dirname(file.path(filepath))
# 현재 작업 디렉터리
getwd() # "C:/Users/.../Documents"
# 단순 텍스트로 저장. 이어서 쓰기 가능
write.table(mtcars, "C:/temp/test.txt", append = TRUE)
# csv로 저장
write.csv(mtcars, "C:/temp/test.csv")
윈도에서 R 파일을 불러오는데 이런 에러가 발생했다. 소스에 한글이 있어서 발생했다.
> source("D:\\test.R")
경고메시지(들):
In grepl("\n", lines, fixed = TRUE) :
이 로케일에서는 입력문자열 17는 유효하지 않습니다
>
source 함수에서 encoding 옵션을 사용해서 해결했다.
source("D:\\test.R", encoding="utf-8")
어떤 함수가 얼마나 걸리는지 알아볼 수 있다.
print(system.time(
main()
))
사용자 시스템 elapsed
1.15 0.00 1.15
plot(mtcars$mpg,mtcars$threshold, type="l", col=cols[1], lty = "dashed",
xlab="seq", ylab="values", ylim=ylim,
main="threshold와 다른 변수들간의 상관관계",
sub=sprintf("최대 허용량 : %s, 초당 요청자수 : %s", MAX_CNT, REQUEST_CNT_PER_SEC),
panel.first = grid()
)
Mozilla Coporation이 Pocket의 개발사인 Read It Later를 완전히 인수했다.
Mozilla의 첫 번째 전략적 인수다. Pocket이 Mozilla의 모바일 점유율을 높이고, 모든 플랫폼에서 사람들이 양질의 콘텐츠를 탐색하고, 접근할 수 있는 강력한 도구라는 것이 Mozilla의 전략에 부합한다고 했다. 이것에 중점을 두어 Mozilla의 제품 라인에 나란히 포함될 것이라고 한다.
이런 전략적 인수의 결과, Pocket은 Mozilla Corporation의 자회사가 될 것이고, Mozilla 오픈 소스프로젝트의 한 부분을 차지할 것이라고 했다.
Pocket : Read It Later 사에서 만들었고, Save-for-Later 서비스를 제공한다. 현재 한 달 1천만 사용자들이 사용하고 있고, Flipboard와 트위터와 같은 사람들이 많이 쓰는 앱 수백개에서 이 서비스가 통합되어있다. 흥미로운 아티클 비디오 등 웹의 콘텐츠를 저장할 수 있는 서비스다. 콘텐츠가 Pocket에 저장되면, 그 콘텐츠는 폰, 태블릿, PC 어느 장치에서든 온-오프라인에 상관없이 볼 수 있다.
Jekyll을 2.4에서 3.x로 업그레이드했다.
# 현재 설치된 버전 확인
bundle show jekyll
/Library/Ruby/Gems/2.0.0/gems/jekyll-2.4.0
# 최신 버전 의존성 확인
gem dependency jekyll -v 3.4.1
Gem jekyll-3.4.1
addressable (~> 2.4)
colorator (~> 1.0)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 1.1)
kramdown (~> 1.3)
liquid (~> 3.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (~> 1.7)
safe_yaml (~> 1.0)
Gemfile을 수정한다.
gem 'jekyll', '~> 3.4.1'
수정 후 변경된 gem을 설치한다.
bundle install
...
Bundle complete! 1 Gemfile dependency, 18 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.
Bundle에 설치된 Gem을 확인했다.
bundle show
Gems included by the bundle:
* addressable (2.4.0)
* bundler (1.10.6)
* colorator (1.1.0)
* ffi (1.9.10)
* forwardable-extended (2.6.0)
* jekyll (3.4.1)
* jekyll-sass-converter (1.5.0)
* jekyll-watch (1.5.0)
* kramdown (1.11.1)
* liquid (3.0.6)
* listen (3.0.8)
* mercenary (0.3.6)
* pathutil (0.14.0)
* rb-fsevent (0.9.7)
* rb-inotify (0.9.7)
* rouge (1.10.1)
* safe_yaml (1.0.4)
* sass (3.4.22)
Jekyll을 실행했는데, 이런 에러가 발생했다.
bundle exec jekyll serve
...
Dependency Error: Yikes! It looks like you don't have /Users/kichuljung/workspace/kichul_blog/_plugins/html_filters.rb or one of its dependencies installed. In order to use Jekyll as currently configured, you'll need to install this gem. The full error message from Ruby is: 'cannot load such file -- nokogiri' If you run into trouble, you can find helpful resources at https://jekyllrb.com/help/!
jekyll 3.4.1 | Error: /Users/kichuljung/workspace/kichul_blog/_plugins/html_filters.rb
Post 목록에서 내용이 길 경우 내용을 잘라주는 플러그인에서 nokogiri gem을 설치하지 않아서 발생한 문제다. 그래서 Gemfile에서 nokogiri gem 주석을 해제했다. 그리고 다시 bundle install을 실행했다.
그리고 다시 Jekyll을 실행했는데, 또 에러가 발생했다.
bundle exec jekyll serve
...
Deprecation: You appear to have pagination turned on, but you haven\'t included the \`jekyll-paginate\` gem. Ensure you have `gems: [jekyll-paginate]` in your configuration file.
...
pagenate 설정이 되어 있어서, jekyll-paginate를 _config.yml 설정에 추가했다.
# 이미 설정된 내용
paginate: 5
paginate_path: "posts/page:num/"
# gem 설정을 추가
gems: ['jekyll-sitemap', 'jekyll-paginate']
Gemfile에 다음 내용을 추가했다.
gem 'jekyll-paginate', '~>1.1.0'
다시 bundle install을 하고, Jekyll을 실행했지만, 또 에러가 발생했다.
...
Dependency Error: Yikes! It looks like you don't have redcarpet or one of its dependencies installed. In order to use Jekyll as currently configured, you'll need to install this gem. The full error message from Ruby is: 'cannot load such file -- redcarpet' If you run into trouble, you can find helpful resources at https://jekyllrb.com/help/!
Conversion error: Jekyll::Converters::Markdown encountered an error while converting '_posts/2013-08-05-About-Ajax.md':
redcarpet
ERROR: YOUR SITE COULD NOT BE BUILT:
------------------------------------
redcarpet
.config.yml 파일에서 redcarpet 쓰는 부분을 모두 제거했다.
markdown: redcarpet
...
redcarpet:
extensions: [
"no_intra_emphasis",
"fenced_code_blocks",
"autolink",
"tables"
# "with_toc_data" # 맥에서 한글로 섹션명을 썼을 때 에러가 발생한다.
]
이렇게 하니 모두 정상적으로 동작했다.
Upgrading from 2.x to 3.x에서 3.x부터 없어진 기능을 볼 수 있다.
Jekyll을 사용하면서 자주 사용하는 Liquid 문법과 유용한 내용을 메모했다.
<!-- 변수 출력 -->
Hello {{name}}
<!-- 직접 문자열 출력 -->
Hello {{ 'world' }}
<!-- jekyll.environment가 "development"가 아닌 경우 -->
{% if jekyll.environment != "development" %}
...
{% endif %}
<!-- page.path가 _posts를 포함 -->
{% if page.path contains '_posts' %}
...
{% endif %}
_include 디렉터리에 있는 파일을 추가하는 기능이다.
<!-- /_include/amp.html 파일 끼워넣기 -->
{% include amp.html %}
이 태그 안에 들어간 내용은 사이트에 출력되지 않는다.
{% comment %}
<!-- 여기 있는 내용은 노출되지 않음. -->
{% endcomment %}
Pygments가 지원하는 언어의 코드 구문을 강조한다.
\`\`\`python
- [naver](http://naver.com)
**A** is B
\`\`\`
liquid 태그를 그대로 보여줄 수 있게 escape 하는 방법이다.
{% raw %}
{{ "test" }}
{% endraw %}
Node.js로 개발할 때 자주 사용하는 코드를 정리했다.
Crypto는 OpenSSL 해시, HMAC, cipher, decipher, 서명, 증명 함수 등의 wrapper 세트를 포함한 암호 기능을 제공하는 모듈이다.
비밀번호를 데이터베이스에 암호화해서 저장할 때 사용할 수 있는 sha256 암호화 예제다.
const crypto = require('crypto'); // ES6 - import crypto from 'crypto';
const secret = 'abcdefg';
const hash = crypto.createHmac('sha256', secret)
.update('I love cupcakes')
.digest('hex');
console.log(hash);
// Prints:
// c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e
// 현재 파일 경로
__filename; // D:\workspace\diagram\main.js
// 현재 디렉터리
__dirname; // D:\workspace\diagram
// 경로 연결
path.join(__dirname, '/test') // /home/dirname/test
// 상대적인 경로로 연결.
path.resolve('/foo/bar', './baz') // /foo/bar/baz
// 경로에 해당하는 속성을 가져온다.(root, dir, base, ext, name)
path.parse("/usr/local/test.jpg")
// 경로에서 파일명만 가져오기
path.basename('/foo/bar/baz/asdf/quux.html') // Returns: 'quux.html'
path.basename('/foo/bar/baz/asdf/quux.html', '.html') // Returns: 'quux'
// 디렉터리 이동. cwd 변경.
process.chdir('/tmp');
// 파일이동, 이름 변경
fs.renameSync(oldPath, newPath);
// 파일 삭제
fs.unlinkSync(path);
gulpfile에 ES6 문법을 사용할 때, 다음 에러가 뜰 수 있다. 예를 들어, import 할 때.
SyntaxError: Unexpected reserved word
babel-core가 없을 때 발생할 수 있다.
# babel 있는지 확인
npm ls babel
# 없으면 설치
npm install babel-core --save-dev
// 응답 헤더 지정
res.set('Content-Type', 'image/png');
// URL 파싱
require('url').parse(req.url)
// 정규식 패턴을 사용한 라우팅 (puml로 끝나는 경우)
app.get(/\.puml$/, function(req, res) {
}
# production으로 모듈 설치
NODE_ENV=production npm install --only=prod
# 개발 디펜던시만 설치
npm install --only=dev
내가 사용했던 유용한 Node.js 모듈이다. 더 많은 모듈을 npmjs에서 검색할 수 있다.
Atom은 이해하기 쉽고, 커스터마이즈 하기 쉬운 에디터다. Github에서 Sublime Text와 상당히 비슷하게 만들었는데, 내장된 패키지 매니저가 있어서 패키지 관리나 설정은 오히려 더 쉽고, 편하다.
그리고 Electron을 사용해서 만들었기 때문에 윈도, OSX, 리눅스에서 모두 동작한다. Electron에서 Webview를 사용할 수 있어서, browser-plus와 같은 패키지를 만들 수 있는 것 같다. 이 부분이 Sublime Text와 비교했을 때 가장 큰 장점일 수 있을 것 같다. 오직 Atom 에디터만 열어놓고 웹 개발을 할 수 있다.
그 밖에 마크다운 관련 패키지가 기본적으로 내장되어 있어서, 별도로 설치할 필요가 없다. CTRL + SHIFT + M을 누르면 프리뷰 화면을 보면서 마크다운 텍스트를 편집할 수 있어서 편하다.
사용하면서 Sublime Text에 있는 패키지는 대부분 찾을 수 있었고, 기능은 오히려 더 좋았다.
Sublime Text와 비슷한 환경을 만들기 위해 추가로 설치했던 패키지와 기능을 간단하게 기술했다. 진하게 표시된 패키지는 특히 유용한 것이다.
패키지 이름 | 기능 |
---|---|
autosave | 포커스를 잃었을 때 파일 저장 |
open-terminal-here | 터미널을 띄워준다. 윈도, 맥에서 모두 동작 |
plantuml-viewer | plantuml 뷰어 |
japanese-wrap | 한글 워드랩이 정상적으로 동작하지 않는 문제를 해결 |
Stylus | Stylus Syntax Highlight |
language-cjsx | coffee jsx(cjsx) Syntax Highlight |
language-lua | lua Syntax Highlight |
language-plantuml | plantuml Syntax Highlight |
Remote-FTP | 원격 파일 관리 (ftp, sftp 지원) |
git-plus | 편리한 git 명령이 가능 |
term | 터미널(윈도에서 tty.js 모듈 빌드 문제 때문에 사용 불가) |
project-manager | Sublime Text처럼 프로젝트 관리 |
browser-plus | 브라우저로 웹사이트를 열 수 있음. |
minimap | 코드 미니맵 |
date | “2015-10-10”와 같은 형태로 날짜 입력 |
highlight-selected | Sublime Text처럼 단어에 더블 클릭했을 때, 선택된 단어를 표시 |
react | react 문법. jsx 지원 |
react-snippets | React 스니펫 |
terminal-panel | 터미널 명령 실행 |
atom-autocomplete-php | PHP 자동완성 |
autocomplete-php | PHP 자동완성 |
atom-coffee-repl | coffee repl 환경 |
atom-terminal | 현재 파일 디렉터리에서 터미널 열기 |
atom-wallaby | Wallaby.js로 유닛테스트 |
coffee-autocompile | 커피 스크립트를 자동으로 컴파일 |
coffee-navigator | 커피 스크립트를 위한 코드 내비게이션 |
hyperclick | Hyperclick UI |
vagrant | 편리한 vagrant 명령. (문제 있음) |
nuclide는 웹과 모바일 개발에 특화되고, Atom 위에 패키지들의 모음으로 만들어진 통합 개발환경이다. 다시 말하면 Hack과 Javascript 개발하기 위한 Nuclide라는 이름으로 시작하는 페이스북이 만든 패키지들의 모음이라 할 수 있다.
기본적으로 원격 개발이 가능하므로, Remote-FTP 패키지는 비활성화시킨 후 사용해야 한다. 언어는 Hack과 Javascript를 지원하고, 정의한 위치로 이동하는 기능과 자동완성을 기본적으로 포함하고 있다. tree-view와 기능이 겹치기 때문에 비활성화하는 것이 좋다.
현재 윈도와 OSX에서 1.0.19를 사용하고 있는데, 몇 가지 문제가 있었다.
에디터를 열고, 사용할 때 전체적으로 Sublime Text보다 느려 답답한 느낌이 있다.
이 문제는 OSX에서 더 심각했다. 처음 Atom을 실행하면 프로세스가 100%로 올라간다. 어느 정도 시간이 지나면 다시 내려간다.
여러 패키지를 업데이트할 때, CPU 부하가 심하다. 아무리 기다려도 업데이트가 완료되지 않는 경우도 발생한다. 사용하다가 검색이 필요해서 CTRL + F를 눌러 검색하려고 했는데, 아무런 반응도 없다. 단축키가 아무것도 동작하지 않는 문제도 있었다. 되살리기 기능도 제대로 안 될 때도 있었다.
이런 문제들 때문에, Atom의 엄청난 기능에도 불구하고, 메인으로 사용하지 않고 있다. 무거운 것은 참고 쓸 수 있지만, 안정성은 포기할 수가 없었다. 마크다운 문서 편집 용도로만 쓰고, 코딩 작업에는 Sublime Text를 사용하고 있다.
개인적으로 기능만 봤을 때, Sublime Text를 앞서는 것 같다. 앞으로 안정성만 좋아진다면 메인으로 사용하고 싶은 에디터이다.
React Hot Loader 사이트에 있는 비디오를 보고, 호기심을 가졌다. 전에 live-reload 기능이 있긴 하지만, 페이지 전체를 다시 불러오는 형태라서 비효율적으로 볼 수도 있다. 에디터에서 수정 후 저장하자마자 깜빡임도 없이 브라우저에 있는 UI가 바로 바뀌는 모습이 인상적이었다. 특히 CSS 수정했을 때 유용해 보였다.
React Hot Loader는 React 컴포넌트를 수정할 때 상태를 잃지 않고, 즉각적으로 live refresh를 할 수 있게 해주는 Webpack 플러그인이다. 그래서 React.js와 Webpack을 사용해야 한다. React.js만 지원한다.
간단하게 Webpack은 모든 Static Web Resource를 하나의 자바스크립트 파일로 만들 목적으로 사용하고, React.js는 페이스북에서 만든 뷰 구현을 돕는 프레임웍이다. 추가로 gulp와 coffee script를 사용했다.
HMR 기능을 사용하려면 Webpack을 사용해야 한다. require, configuration, cli 3가지 방법으로 로더를 사용할 수 있다.
# webpack 설치
npm install webpack -g
CLI 방식:
# entry.js 파일을 분석해서 bundle.js 파일을 만든다.
webpack ./entry.js bundle.js
# .jade 파일에 대해 jade 로더, .css 파일에 대해 style 로더 사용
webpack --module-bind jade --module-bind 'css=style!css'
# png 파일에 대해 url 로더 사용하고, mimetype을 image/png로 설정했다.
webpack --module-bind "png=url-loader?mimetype=image/png"
require로 사용하는 방법:
require("./loader!./dir/file.txt");
require("jade!./template.jade");
!로 리소스와 로더를 분리할 수 있다.
configuration 방식 :
설정해야 할 것이 많기 때문에, configuration을 정의한 후 gulp에서 사용하는 방식이 좋을 것 같다.
각각의 로더는 모두 설치해야 한다. devDependency로 두는 것이 좋다.
npm install webpack css-loader style-loader --save-dev
HMR 기능을 사용하기 위해서 간단하게 데몬을 띄울 수 있다.
npm install webpack-dev-server -g
npm install component-webpack-plugin
webpack-dev-server ./entry --hot --inline --module-bind "css=style!css"
CSS 작업만 한다면 이렇게 CLI 모드를 사용해서 작업할 수 있다.
inline과 hot 옵션은?
HotModuleReplacementPlugin을 두 번 추가하지 않도록 주의해야 한다.
윈도에서 경로 문제
윈도에서 Webpack 개발 서버가 제대로 변경된 파일을 감시하지 못하는 문제가 있었는데 설정 파일에서 경로를 다음과 같이 변경해야 한다.
// context: __dirname + "/src", // OSX or Linux
context: __dirname + "\\src", // 윈도용<
서브라임텍스트는 개발할 때 주로 사용하는 에디터인데, 기본 편집기능도 좋고, 패키지를 사용하게 되면 훨씬 더 편하게 개발할 수 있다. 이 포스팅에서는 개발할 때 자주 사용하는 패키지를 소개할 것이다.
평소 서버 구성도, 시퀀스 다이어그램을 그려야 하는 경우가 많이 있다. Dia, 파워포인트, Pencil, yED와 같은 툴을 사용해봤지만, 만족스럽지 않았다. 직접 그리는 방식이라서 배치하는데 신경이 많이 쓰여 흐름이나 배치에 집중하기 어려웠다.
이 플러그인은 다음 프로그램이 준비되어 있어야 한다.
그리고 서브라임 패키지 인스톨러로 설치가 되지 않기 때문에, 패키지 디렉터리에 직접 설치해야 한다.
# osx
cd ~/Library/Application\ Support/Sublime\ Text\ 3/Packages
# windows
cd "%APPDATA%\Sublime Text 3\Packages"
# 플러그인 소스 가져오기
git clone https://github.com/jvantuyl/sublime_diagram_plugin
서브라임 텍스트에서 git을 편하게 사용할 수 있게 하는 플러그인이다. 패키지 인스톨러를 통해 설치하면 된다.
SublimeGit
윈도를 사용할 경우 따로 git 프로그램을 설치해야 한다. 그리고 경로를 다음과 같이 설정해야 할 것이다.
{
"git_executables": {
"git": ["c:/Program Files/Git/bin/git"],
"git_flow": ["c:/Program Files/Git/bin/git-flow"],
"legit": ["c:/Program Files/Git/bin/legit"],
"gitk": ["c:/Program Files/Git/bin/gitk"]
}
}
커맨드 팔레트에서 주로 다음 3개만 사용하는데, 특히 status에서 선택하는 기능이 좋다.
Markdown 형식으로 메모도 하고, 프로젝트에서 Readme.md 문서도 자주 만드는데, Markdown 파일을 작성하는데, 이 플러그인이 아주 유용하다. 지금 작성하는 블로그도, 서브라임에서 이 플러그인 사용해서 작성하고 있다. 문서와 코드(Javascript, Python 등)에 신택스 하이라이트도 되는 것이 핵심이다.
패키지 팔레트에서 다음 이름의 플러그인을 찾아서 설치하면 된다.
MarkdownEditing
Markdown Editing으로 Markdown 파일을 작성할 때 도움이 된다고 할 수 있다. 최종 결과물을 브라우저에서 볼 수 있게 하는 플러그인이 바로 Markdown Preview다. 작성하다가 최종 결과물이 보고 싶다면 CTRL+B 윈도에서 빌드하면 브라우저에 작성하고 있는 문서가 보일 것이다.
Markdown Preview
여러 서버에서 작업할 일이 많이 있는데, 이 플러그인이 아주 유용하다.
SFTP
sftp-config.json 파일을 만들고, CTRL + ALT + U, W를 누르면 서버에 존재하는 폴더와 파일을 볼 수 있다. 수정할 파일을 선택하고, 수정 작업을 거친 후 저장하면 서버에 저장이 된다. 그런데 이 플러그인을 쓰는 것보다 atom 에디터의 SFTP 기능이 더 좋았다.
{
...
"type": "sftp",
"save_before_upload": true,
"upload_on_save": false,
"sync_down_on_open": false,
"sync_skip_deletes": false,
"confirm_downloads": false,
"confirm_sync": true,
"confirm_overwrite_newer": false,
"host": "127.0.0.1",
"user": "아이디",
"password": "비밀번호",
"port": "43520",
"remote_path": "/",
"ignore_regexes": [
"\\.sublime-(project|workspace)", "sftp-config(-alt\\d?)?\\.json",
"sftp-settings\\.json", "/venv/", "\\.svn", "\\.hg", "\\.git",
"\\.bzr", "_darcs", "CVS", "\\.DS_Store", "Thumbs\\.db", "desktop\\.ini"
],
...
}
브라우저의 텍스트 입력창(Textarea)의 내용을 서브라임 텍스트로 작성할 수 있게 하는 플러그인이다. 크롬이나 파이어폭스에 플러그인을 추가로 설치해야한다. 크롬과 파이어폭스에 플러그인을 설치하고, 텍스트 영역에 GhostText 기능을 활성화할 수 있다. 그 상태에서 서브라임에서 내용을 작성할 수 있다.
그러나 내가 자주 사용하는 도구는 위지웍 에디터라서 잘 활용하지 못했다.
GhostText
처음 알았을 때는 많이 사용하려고 시도했지만, 은근히 귀찮다. 텍스트 창에 글을 입력하다 브라우저가 문제가 생겨 종료되더라도, 글은 서브라임에 남아있는 것은 장점이다.
2016-06-22와 같이 원하는 형식의 날짜를 단축키로 입력할 수 있게 하는 플러그인이다. 메모하다가 오늘 날짜를 입력해야 할 때 유용하다.
InsertDate
단축키는 CTRL + F5, D
프로젝트에서 사용하는 언어에 따라 linter를 세팅하고 시작하는 것이 좋다.
coffeelint
eslint
jshint
pylint
shellcheck
…
Dockerfile 문법 강조
Autotools 문법 하이라이트
JSX를 포함한 ES6 문법 하이라이트
괄호 안에 있을 때, 좌측에 표시해 준다.
rpm.spec 파일에 관한 문법 강조를 지원한다.
프로젝트에 따라 문법을 다르게 설정할 수 있다.
Plantuml을 이미지로 만들어준다. 내부에서 Java와 Graphviz를 사용한다.
현재 파일 또는 프로젝트 폴더를 터미널로 열어준다.
Stylus 문법 강조를 지원한다.
Liquid 문법 강조를 지원한다.
사이드바 기능을 강화한다. 특히 우클릭을 통해 다양한 기능을 사용할 수 있다.
React로 개발할 때 아주 유용한 Snippet을 제공한다. (cdm, cdup, …)
R 파일 작업할 때, 자동완성, 메뉴, 팝업 힌트를 제공.
에디터에서 코드 커버리지 데이터를 표시한다. 테스트가 된 코드는 녹색으로 나온다.
PHP CodeSniffer, PHP Code Beautifier and Fixer, PHP Coding Standards Fixer, the PHP Linter, PHP Mess Detector, Scheck
문서화 주석을 빠르게 작성할 수 있다. /**로 시작하면 자동 완성됨.
Nullsoft Scriptable Install System 문법 강조
Lua 자동완성
Jekyll과 통합
INI 문법 강조
현재 폴더, 프로젝트에서 검색할 수 있다.
문서화를 위한 주석을 쉽게 작성할 수 있다.
C++11 문법 정의
C++에서 유용한 스니펫이 추가됨.
이번 장은 플러그인 너무 많아서 지금은 쓸 일 없는 것들을 지우면서 기록한 내용이다.
현재 서브라임탭에 있는 것을 브라우저로 보여준다.
Typescript 자동완성.
VBScript 문법 강조, 스니펫
정규식으로 영역을 선택할 수 있다.
반복적인 작업을 할 때 사용한다.
Markdown, HTML 파일을 서비스할 수 있는 서버를 띄운다.
Babel 플러그인에서 제공해서 필요 없다.
React Templates 문법 강조. Babel 플러그인에서 제공해서 필요 없다.
커맨드 팔레트 명령을 통해 탭이 이동한다.
TOP 100. 마크다운 문법 강조. 언어에 따라 강조.
인덴트와 코드의 위치를 상태바에 보여준다. JS와 PHP는 안 된다. 어떤 언어에서 동작하는지 모르겠다.
HTML5 번들. HTML5 문법 모드와 스니펫.
Flow로 자바스크립트 정적 분석을 한다.
특정 URL의 데이터를 새로운 또는 이미 존재하는 버퍼로 가져온다.
Dockerfile 문법 강조
색상을 미리 보고, 선택할 수 있다.
Atlassian Confluence와 통합된 플러그인.
브라우저와 서브라임을 통합하는 플러그인이다. 우선 서브라임에서 브라우저를 실행하고, 서브라임에서 브라우저로 커맨드를 날릴 수 있다. reload, 스타일시트 보기, 특정 코드 실행하기 등이 가능하지만, 아직 개발 중이라고 한다.
현재 창에 열린 모든 파일에 있는 내용과 일치할 때 자동완성 키워드를 보여주는 플러그인이다. 느려질 수 있어서 주의가 필요하다.
파일 구문을 감지하고, 적용할 수 있는 플러그인이다. 예를 들면, .rb 파일은 일반적으로 Ruby 파일이지만 Rails 프로젝트에서의 Rspec, Cucumber 파일, Ruby on Rails(컨트롤러, 모델) 파일을 구분할 수 있다.
신택스 하이라이트, 체크, 커맨드, 단축키, watch 기능 등을 제공한다.
Coffee Script JSX 문법을 지원하는 플러그인이다. ES6로 바꾼 이후는 쓸 필요 없었다.
Grunt와 통합하는 플러그인이다. Gulp로 바꾸고 나서 쓰지 않는다.
’ 또는 “에 커서를 놓고, 이 플러그인 커맨드를 실행하면 ‘ -> “, 아니면 그 반대로 바꿔주는데, 잘 사용하지 않아서 지웠다.
https://packagecontrol.io/packages/ChangeQuotes
CSS 작업할 때, 컬러 코드를 인식해서 색상을 보여주고, 선택할 수 있는 툴팁을 제공한다. 인라인으로 색상을 보여주는 부분 때문에, 확실히 느려진다.
https://packagecontrol.io/packages/ColorHelper
Emmet의 공식 플러그인이다. Emmet은 예전에 Zen Coding으로 불렸고, html 작업을 할 때, CSS 스타일로 입력해서 html을 만들어 준다. 편리하지만 html 작업을 많이 하지 않고, 그마저도 React로 하므로 필요 없어졌다.
https://packagecontrol.io/packages/Emmet
Evernote를 서브라임에서 쓸 수 있는 플러그인이다.
https://packagecontrol.io/packages/Evernote
서브라임에서 선택한 키워드를 구글에서 검색하는 플러그인이다.
https://packagecontrol.io/packages/Google%20Search
npm을 통합한 플러그인이다. 셸로 하는 게 더 편하다.
https://packagecontrol.io/packages/npm
yUML을 서브라임에서 만들어 주는 플러그인이다. plantuml을 사용해서 지웠다.
https://packagecontrol.io/packages/yUML
Git 통합 플러그인이다. 셸에서 Git 작업을 하면서 지웠다.
https://packagecontrol.io/packages/SublimeGit
SublimeGit과 유사한 플러그인이다.
https://packagecontrol.io/packages/GitSavvy
HTML을 보기 좋게 만드는 도구다. 가끔 필요해서 같은 기능을 하는 사이트를 이용하면서 지웠다.
https://packagecontrol.io/packages/HTMLBeautify
LiveReload 서버가 서브라임텍스트에서 실행되고, 파일을 저장할 때, 이 서버와 통신할 수 있다.
https://packagecontrol.io/packages/LiveReload
SQL을 사람한테 보기 좋게 만드는 플러그인이다.
https://packagecontrol.io/packages/SqlBeautifier
반복적인 작업을 쉽게 할 수 있게 돕는 플러그인이다.
https://packagecontrol.io/packages/Text%20Pastry
JSON을 보기 좋게 만들어주고, 유효성을 체크할 수 있고, 압축할 수도 있다.
https://packagecontrol.io/packages/Pretty%20JSON
포맷에 맞춰서 현재 날짜와 시간을 출력하는 플러그인이다.
https://packagecontrol.io/packages/InsertDate
SVN 통합 플러그인.
https://packagecontrol.io/packages/SVN
셸 명령을 서브라임내에서 실행하고, 그 결과를 탭에서 보여주는 플러그인이다.
https://packagecontrol.io/packages/ShellCommand
ShellCommand와 유사한 플러그인이다. 다른 점은 결과가 서브라임 콘솔에 출력된다.
https://packagecontrol.io/packages/Shell%20Turtlestein
탭에 추가 기능을 주는 플러그인이다. sticky 탭을 만들고, 탭 정렬, 삭제, 이름 변경 등을 할 수 있다.
https://packagecontrol.io/packages/TabsExtra
Scheme이라는 언어를 지원하는 플러그인인데, 언제, 왜 설치했는지 모르겠다.
https://github.com/egrachev/sublime-scheme
Emmet 플러그인 설치할 때 자동으로 설치되는 플러그인이다.
https://github.com/emmetio/pyv8-binaries
Console, DOM, Loop 등을 쉽게 작성할 수 있는 Snippet이다. cl은 console.log, al은 alert, …
https://packagecontrol.io/packages/JavaScript%20%26%20NodeJS%20Snippets
Handlebars.js 템플릿을 지원하는 플러그인이다.
https://packagecontrol.io/packages/Handlebars
Jade 템플릿을 지원하는 플러그인이다.
https://packagecontrol.io/packages/Jade
Liquid 템플릿을 지원하는 플러그인이다.
https://packagecontrol.io/packages/Liquid
Less Syntax Highlight를 지원하는 플러그인이다.
https://packagecontrol.io/packages/LESS
Open Komodo Editor에서 이식된 플러그인으로, 다양한 언어를 지원한다. 심볼이 정의된 파일로 이동하고, 사용할 수 있는 모듈을 자동 완성할 수 있다.
https://packagecontrol.io/packages/SublimeCodeIntel
Swift 언어를 지원하는 플러그인이다. XCode에서 작업해서 필요 없다.
https://packagecontrol.io/packages/Swift
서브라임 탭에서 PHP, Python, Ruby, R, Node.js 등 다양한 언어의 인터프리터를 실행하는 플러그인이다.
https://packagecontrol.io/packages/SublimeREPL
Vue 템플릿을 지원하는 플러그인이다.
https://packagecontrol.io/packages/Vue%20Syntax%20Highlight
TypeScript 문법, Snippet을 지원하는 플러그인이다. 백그라운드로 Node 프로세스가 실행된다.
https://packagecontrol.io/packages/TypeScript
로그 파일을 볼 때 로그 단계에 따라서 심각하면 빨강 덜 심각하면 노랑으로 표시하는 플러그인이다.
https://packagecontrol.io/packages/LogView
Coffee Script 문법을 체크하는 Linter인데, ES6로 바꾼 후 사용하지 않는다.
https://packagecontrol.io/packages/SublimeLinter-coffeelint
JSX 언어 정의.
PostgreSQL, MySQL, SQLite, MariaDB 같은 데이터베이스에서 질의를 실행하는 플러그인.
.env 파일에 대한 문법 강조
Document Object Model의 약자로 HTML을 제어할 수 있는 API의 역할을 한다.
이러한 DOM 메소드를 어떻게 사용하는지 알아본다.
<h1>testByTagName - 색 변경 - getElementsByTagName 예제</h1>
<ul id="test1">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<h1>testByName - 색 변경, getElementsByName 예제</h1>
<ul id="test2">
<li name="li_name">1</li>
<li name="li_name">2</li>
<li name="li_name">3</li>
<li name="li_name">4</li>
<li name="li_name">5</li>
</ul>
<h1>drawBox - 박스 그렸다 5초만에 지우기</h1>
<script type="text/javascript">
//DIV 박스를 body 태그 안에 추가해주는 함수
function drawBox() {
// DIV 요소 생성
var div = document.createElement("div");
// DIV의 속성 지정 (id, style)
div.setAttribute("id","testDiv");
div.setAttribute("style","border:1px solid #000000;padding:10px;height:100px;");
// DIV 내용으로 위에서 지정한 속성을 출력
div.innerHTML = div.getAttribute("style");
// DIV를 body 태그안에 추가해준다.
document.body.appendChild(div);
// 5초 보여주고 지우기
setTimeout(function() {
// 요소 제거
document.body.removeChild(div);
},5000);
}
function testByTagName() {
// test1이란 아이디를 가진 요소를 JS 변수로 선언
var test2 = document.getElementById("test1");
// test2에서 tagName으로 검색해서 li인 것만 찾아서 배열로 저장
var arrLi = test2.getElementsByTagName("li");
// 배열 갯수
var cnt = arrLi.length;
// 배열 루프
for(var i=0;i<cnt;i++) {
// 선택된 li 요소의 색상 변경
arrLi[i].setAttribute("style","color:red;");
}
}
function testByName() {
var arrLi = document.getElementsByName("li_name");
// 배열 갯수
var cnt = arrLi.length;
// 배열 루프
for(var i=0;i<cnt;i++) {
// 선택된 li 요소의 색상 변경
arrLi[i].setAttribute("style","color:blue;font-size:14px;");
}
}
// 함수 실행
drawBox();
testByName();
testByTagName();
</script>
이제 텍스트 에디터를 이용해서 helloworld.html 파일을 생성하고, 아래 코드를 입력하고 저장한다.
<html>
<head>
<title>Hello World</title>
</head>
<body>
<script type="text/javascript">
console.log("Hello World!");
</script>
helloworld.html 파일을 파이어폭스에서 불러온다. (탐색기에서 파이어폭스로 드래그)
지금까지 파이어버그의 콘솔 창을 이용해서 Hello World를 출력했다. 여기서 중요한 것은 console.log를 이용한 디버깅인데, 위의 예제는 String 값을 출력하는 것이지만 Object나 Array를 넣게 되면 안에 들어가는 내용이 무엇인지 보기 좋게 나오게 된다.
이러한 디버깅 방법은 Opera의 기본 디버거인 잠자리나 크롬의 개발자도구에서 별도의 플러그인 없이 지원하며, IE8 개발자도구에서도 String까지는 지원한다. 게다가 모바일 사파리에서도 디버그 설정을 활성화하면 console.log를 사용해서 디버깅할 수 있다.
자바스크립트로 모달창을 만들려고 한다.
모달창은 보통 배경은 어둡고, 팝업 레이어가 뜨는 형태로 많이 사용한다. 모달창을 만들기 위해서 모달창에 어떤 기능을 넣을지 정의 해야 한다.
간단하게 기능 정의를 했다. 이 정도 기능을 가진 모달창을 어떻게 만드는지 알아본다.
먼저 HTML과 CSS를 정의한다.
<!-- 모달창을 여는 버튼 -->
<button id="button">Open Modal</button>
<!-- 모달창 -->
<div id="modal">
<h3>Test Modal</h3>
<p>이 창은 모달창입니다.</p>
<button id="confirm_button">확인</button>
<button class="js_close">닫기</button>
</div>
#modal {display:none;background-color:#FFFFFF;position:absolute;top:300px;left:200px;padding:10px;border:2px solid #E2E2E2;z-Index:9999}
버튼이 하나가 있고, 감춰진 레이어(모달창)가 있다. 버튼을 누르면 감춰진 레이어가 모달창으로 나타나고, 확인 버튼이나 닫기 버튼을 누른다는 것이 예상되는 HTML이다.
<!-- 스크립트 영역 -->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" src="/Example.Modal.js"></script>
<script type="text/javascript">
// 모달창 인스턴트 생성
var myModal = new Example.Modal({
id: "modal" // 모달창 아이디 지정
});
// 모달창 여는 버튼에 이벤트 걸기
$("#button").click(function() {
myModal.show(); // 모달창 보여주기
});
// 모달창 안에 있는 확인 버튼에 이벤트 걸기
$("#confirm_button").click(function() {
alert("나는 모달창이다.");
myModal.hide(); // 모달창 감추기
});
</script>
자바스크립트를 살펴보면, 3가지 액션을 한다.
if(typeof(Example) == "undefined") var Example = {};
(function($){
Example.Modal = function() {
this.initialize.apply(this,arguments);
}
Example.Modal.prototype = {
initialize : function(hash) {
var obj = this;
// 멤버변수 정의
this.hash = this.getHashData(hash);
this.width = 0; // 콘텐츠 레이어 너비
this.height = 0; // 콘텐츠 레이어 높이
this.c_width = 0; // body 화면 너비
this.c_height = 0; // body 화면 높이
this.s_width = 0; // body 전체 너비
this.s_height = 0; // body 전체 높이
// 콘텐츠 레이어 크기 구하기(멤버변수에 저장)
this.getLayerSize();
// body 크기 구하기(멤버변수에 저장)
this.getBodySize();
// 추가 요소 생성
this.addElement();
// 마우스, 키 이벤트 정의
this.addEvent();
},
// Hash 변수정의
getHashData : function(hash) {
if(typeof(hash.id) == "undefined") hash.id = "modal"; // 개체 아이디
if(typeof(hash.is_slide) == "undefined") hash.is_slide = 0; // 슬라이딩 여부
return hash;
},
// 추가 요소 생성 - JS에서 추가해 주어야 할 HTML 작성합니다.
addElement : function() {
// 배경 레이어 HTML 받아오기
var html = this.addOverlay();
// 배경 레이어 HTML을 콘텐츠레이어 앞에 추가
$("#"+this.hash.id).before(html);
},
// 마우스, 키 이벤트 정의
addEvent : function() {
var obj = this;
// 닫기 버튼 클릭 이벤트 정의
$("#"+this.hash.id+" .js_close").click(function() {
// 모달창 감추기(여기서 obj는 Example.Modal 인스턴트 자체를 의미)
obj.hide();
});
},
/* 주요기능 */
// 모달창 보여주기
show : function() {
// 가운데로 이동
this.moveCenter();
// 배경 레이어 적용
this.applyOverlay();
// 콘텐츠 레이어 보여주기
$("#"+this.hash.id).show();
// 배경 레이어 보여주기
$("#"+this.hash.id+"_overlay").show();
},
// 모달창 감추기
hide : function() {
$("#"+this.hash.id).hide(); // 콘텐츠 레이어 감추기
$("#"+this.hash.id+"_overlay").hide(); // 배경 레이어 감추기
},
/* 부가 기능 */
// 콘텐츠 레이어의 너비와 높이를 구해서 멤버변수에 정의
getLayerSize : function() {
this.width = $("#"+this.hash.id).outerWidth();
this.height = $("#"+this.hash.id).outerHeight();
},
// body 크기 구해서 멤버변수에 정의
getBodySize : function() {
this.c_width = document.documentElement.clientWidth;
this.c_height = document.documentElement.clientHeight;
this.s_width = document.documentElement.scrollWidth;
this.s_height = document.documentElement.scrollHeight;
},
// 배경 레이어 HTML 생성
addOverlay : function() {
var html = "";
html += "<div id='"+this.hash.id+"_overlay' style='display:none;width:100%;position:absolute;top:0px;left:0px;opacity:0.5;background-color:#000000;z-Index:999'></div>";
return html;
},
// 콘텐츠 레이어를 가운데로 이동(top, left 조절해 줌)
moveCenter : function() {
// left 좌표 구하기
var left = Math.floor((this.c_width-this.width)/2);
// top 좌표 구하기
var res_height; // 콘텐츠를 화면상의 가운데로 두었을 때의 높이
if(this.c_height < this.height) { // 화면 높이 < 콘텐츠 레이어 높이
res_height = 0;
} else { // 화면 높기 >= 콘텐츠 레이어 높이
res_height = Math.floor((this.c_height-this.height)/2); // 차이를 빼서 2로 나눔. 그리고 내림.
}
var top = res_height+$(document).scrollTop(); // 화면상의 높이에 스크롤높이를 더한 절대좌표를 top에 저장
// css의 top,left 조정
$("#"+this.hash.id).css("top",top);
$("#"+this.hash.id).css("left",left);
},
// 배경 레이어 적용
applyOverlay : function() {
// body 크기 구하기(멤버변수에 저장)
this.getBodySize();
// 배경 레이어에 width, height css 값 조정
$("#"+this.hash.id+"_overlay").css("width",this.s_width);
$("#"+this.hash.id+"_overlay").css("height",this.s_height);
}
}
})(jQuery);
이 메소드가 어떤 일을 하는지 알아봤다.
인스턴트를 생성했을 때 초기화를 시켜주는 생성자다. 인스턴트 생성되었을 때 하는 모든 일은 여기서 정의한다. getLayerSize(), getBodySize(), addElement(), addEvent() 메소드를 실행한다.
다음과 같은 부분이 있는데, getHashData 메소드를 실행시키고 그 반환 값을 멤버 변수인 this.hash에 집어넣는다는 의미다. 이 메소드는 자바스크립트 인스턴트의 기본(default)값을 정의하는 역할을 한다.
this.hash = this.getHashData(hash);
추가 요소를 생성한다. 모달창 div 나 버튼 같은 요소들은 Javascript로 처리되기 전에 있어도 상관없지만, 백그라운드 레이어 같은 경우는 모달창이 뜰 때만 필요한 부분일 수도 있기 때문에 동적으로 생성되는 게 더 효율적이라고 생각한다.
모듈에 관련된 요소들에 이벤트를 걸어준다. 여기서는 모달창 모듈의 닫기 버튼에 클릭 이벤트를 걸었다.
가장 중요한 보여주기 기능이다. 콘텐츠 레이어를 가운데로 옮기고, 배경 레이어 크기 조정 후 콘텐츠와 배경 레이어를 보여준다.
감추는 기능이다. 모달창과 배경 레이어를 감추는 내용이다.
콘텐츠 레이어의 너비와 높이를 구하는 메소드다. 가운데 정렬하기 위해서 필요한 수치이기 때문에 이 메소드를 따로 두게 되었다.
body의 너비와 높이를 구하는 메소드다. 이 메소드 역시 가운데 정렬을 하기 위해 필요하다.
단순히 배경 레이어를 생성하고, CSS를 in-line으로 정의되어서 독립적으로 쓸 수 있다.
위 메소드를 실행시켜 구한 수치들을 이용해서 계산하는 메소드다. 마지막에는 CSS의 top, left의 수치를 변화시켜서 가운데 정렬을 하게 된다.
배경 레이어의 크기를 body의 크기에 따라서 변화한다. 이 메소드는 resize를 할 때 body의 크기가 달라졌을 경우 실행하도록 하면 항상 body를 덮는 배경 레이어를 만들 수 있다.
웹페이지에서 사용자가 클릭하거나 키보드 입력과 같은 상호작용을 하게 되는데, 클릭이나 키 입력과 같이 발생하는 것을 이벤트라고 한다. 창 크기를 조절하거나 스크롤을 내리거나 올릴 때도 이벤트는 발생하고, 이벤트가 발생했을 때 실행되는 메소드를 이벤트 핸들러라고 한다.
element.attachEvent(event, callback);
element.addEventListener(event, callback, useCapture);
IE와 표준브라우저에서 사용할 수 있는 함수를 만들겠다.
function addEvent(ele,evt,callback, useCapture) {
if(ele.addEventListener) {
ele.addEventListener(evt,callback, useCapture);
} else if(ele.attachEvent) {
ele.attachEvent("on"+evt, callback);
}
}
이벤트는 최상위 엘리먼트로부터 이벤트가 발생한 엘리먼트까지 찾아 들어간 후 다시 최상위 요소까지 올라가며 이벤트를 발생한다. 여기서 이벤트가 발생한 엘리먼트까지 들어가는 과정을 이벤트 캡처라 하고, 최상위 요소까지 다시 올라가는 과정을 이벤트 버블링이라고 한다.
지금까지 얘기한 이벤트를 실제 사용한 예제를 만들겠다.
<h1>Event Test</h1>
<div id="wrap">
<div id="wrap2">
<button id="test_btn">click</button>
</div>
</div>
<h1>DEBUG <button id="clear">clear</button></h1>
<div id="console"></div>
#wrap {border:1px solid #000000;padding:10px;margin-bottom:10px;}
#wrap2 {border:1px solid #E2E2E2;padding:10px;margin-bottom:10px;}
#console {height:400px;overflow-y:scroll;border:1px solid #E2E2E2;padding:10px;}
<!-- 스크립트 영역 -->
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript">
// HTML 엘리먼트를 자바스크립트 변수로 저장
var wrap = document.getElementById("wrap"); // 감싸는 레이어
var wrap2 = document.getElementById("wrap2"); // 감싸는 레이어2
var clear_btn = document.getElementById("clear"); // 클리어 버튼
var test_btn = document.getElementById("test_btn"); // 클릭 버튼
var consol_div = document.getElementById("console"); // 콘솔창
// 카운트 변수 정의
var logCnt = 1;
// 로그를 출력하기 위한 함수
function logConsole(msg) {
consol_div.innerHTML = logCnt+" : "+msg+"<br />"+consol_div.innerHTML;
logCnt++;
}
// 로그 초기화 함수
function clearConsole() {
consol_div.innerHTML = "";
logCnt = 1;
}
// 이벤트 등록을 위한 브라우저 호환을 위한 함수 정의
function addEvent(ele,evt,callback, useCapture) {
if(ele.addEventListener) {
ele.addEventListener(evt,callback, useCapture);
} else if(ele.attachEvent) {
ele.attachEvent("on"+evt, callback);
}
}
/*** 구현 ***/
// 로그 clear 버튼 클릭 이벤트 걸기
addEvent(clear_btn,"click", function(e) {
clearConsole();
},false);
// 감싸는 레이어 클릭 이벤트 걸기
addEvent(wrap,"click", function(e) {
logConsole("wrap click");
},false);
//감싸는 레이어2 클릭 이벤트 걸기
addEvent(wrap2,"click", function(e) {
logConsole("wrap2 click");
},false);
// 버튼 클릭 이벤트 걸기
addEvent(test_btn,"click", function(e) {
// 표준 브라우저일 경우
if(typeof(e.stopPropagation) == "function") {
e.stopPropagation();
} else { // IE 일 경우
window.event.cancelBubble = true;
}
logConsole("button click");
},false);
</script>
자바스크립트로 OOP 방식 개발에 필요한 개념을 알아보려고 한다.
경량의 데이터 교환방식으로 자바스크립트에서는 객체에 속한다.
// 예
var rss = [
{no: 1, title: "블로그글 제목1", content: "블로그 내용1", wdate: "2011-06-25"},
{no: 2, title: "블로그글 제목2", content: "블로그 내용2", wdate: "2011-06-27"}
];
JSON1에 대한 자세한 내용은 다음 링크를 참고한다.
OOP는 대상을 객체로 정하고 이를 다루는 개념이다. 자바스크립트가 OOP를 기본적으로 지원하지 않지만, OOP처럼 개발할 수 있다.
기타 OOP와 관련된 자세한 내용은 관련 서적이나 참고 자료가 많이 있으니 그것을 참고하면 될 것 같다.
이제부터 자바스크립트에서 객체지향 프로그래밍의 형태로 개발할 것이다. 아래 패턴은 모듈이나 페이지를 개발하기 위해 만들었는데, 다른 형태로도 OOP를 구현할 수 있다.
if(typeof(Module) == "undefined") var Module = {};
(function($){
Module.Template = function() {
this.initialize.apply(this,arguments);
}
Module.Template.prototype = {
initialize : function(hash) {
var obj = this;
this.hash = this.getHashData(hash);
this.addElement();
this.addEvent();
},
getHashData : function(hash) {
if(typeof(hash.id) == "undefined") hash.id = "";
return hash;
}
}
})(jQuery);
Module.Template 클래스에는 3개의 메소드로 구성되어 있다.
이제 위의 패턴을 이용해서 간단한 예제를 만들고, 실행할 것이다.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>OOP 테스트 프로그램</title>
</head>
<body>
<button id="person1">신짱구</button>
<button id="person2">나미리</button>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script type="text/javascript" src="data.js"></script>
<script type="text/javascript" src="Module.Example.js"></script>
<script type="text/javascript">
// 인스턴스 생성
var P1 = new Module.Person({ // 신짱구
id: "person1",
data: data1
});
var P2 = new Module.Person({ // 나미리
id: "person2",
data: data2
});
</script>
</body>
</html>
// 데이터 정의
var data1 = {
name: "신짱구",
age: 5
}
var data2 = {
name: "나미리",
age: 22
}
// 클래스 정의
if(typeof(Module) == "undefined") var Module = {};
(function($){
Module.Person = function() {
this.initialize.apply(this,arguments);
}
Module.Person.prototype = {
initialize : function(hash) {
var obj = this;
this.hash = this.getHashData(hash);
this.addEvent();
},
getHashData : function(hash) {
if(typeof(hash.id) == "undefined") hash.id = "";
if(typeof(hash.data) == "undefined") hash.data = {};
return hash;
},
addEvent : function() {
var THIS = this;
$("#"+this.hash.id).mouseover(function() {
THIS.alertName();
}).mouseout(function() {
THIS.alertAge();
});
},
alertName : function() {
console.log("내 이름은 "+this.hash.data.name+"입니다.");
},
alertAge : function() {
var who;
if(this.hash.data.age > 19) {
who = "어른";
} else {
who = "미성년자";
}
console.log(this.hash.data.age+"살 "+who+"에요.");
}
}
})(jQuery);
location.hash = "name=한글";
이런 식으로 입력하면 아이패드 사파리에서는 자동으로 인코딩된다.
location.href = location.href+"#name=한글"
이런 식으로 입력하면 한글이 인코딩 안 된 채로 저장된다.
클래스명을 가지고 요소를 선택할 때 사용하는 메소드로 웹킷 전용 메소드다. getElementsByTagName과 같이 선택된 요소의 배열을 반환하는데, 이 메소드를 쓰는 것이 따로 JS 라이브러리로 구현하거나 XPATH를 사용하는 것보다 엄청나게 빠르다.
IE 9에서는 이 메소드를 지원한다. 반면에 IE 6, 7, 8은 지원하지 않기 때문에 getElementsByTagName 함수를 직접 만들었다.
// 미지원 브라우저에서는 메소드 정의
if(typeof(document.getElementsByClassName) !== "function") {
document.getElementsByClassName = function(className) {
var arr = [];
var ele = document.getElementsByTagName("*");
var cnt = ele.length;
for(var i=0;i<cnt;i++) {
arr.push(ele[i]);
}
return arr;
};
}
대화식 웹 애플리케이션 제작을 위해 아래와 같은 조합을 이용하는 웹 개발 기법이다.
Asynchronous Javascript and XML - 비동기적인 자바스크립트와 XML
기본적으로 Ajax 요청은 도메인이 같은 호스트로만 요청이 가능하고, 도메인이 다른 호스트에 요청하게 되면 콘텐츠를 받아올 수 없다. 예를 들어 aaa.com이라는 호스트에서 http://bbb.com/rss.xml 을 AJAX 요청해서 받아 올 수는 없다. 이를 해결할 수 있는 4가지 방법이 있다.
<div id="result" style="width:800px;height:60px;overflow-y:auto;border:1px solid #000000;padding:10px;margin:0 auto;margin-bottom:10px;"></div>
<div style="display:none;width:800px;height:20px;margin:0 auto;" id="loading">Loading.</div>
<ul id="list"></ul>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.5.min.js"></script>
<script>
// 타이머
var timer = null;
// 로딩 시작 처리
function startLoading() {
$("#loading").show();
timer = setInterval(function() {
$("#loading").append(".");
},100);
}
// 로딩 끝 처리
function stopLoading() {
$("#loading").hide();
clearInterval(timer);
timer = null;
}
// 로딩 시작하기
startLoading();
// Ajax 불러오기
$.ajax({
url: "http://api.flickr.com/services/rest/?method=flickr.interestingness.getList&api_key=d7cf8651384b2e99038ecf497b440421&format=json&nojsoncallback=1&auth_token=72157627258910199-5bc337cbd1122195&api_sig=9de8ad17087c2026f670fb26478c48bb",
type: "get",
dataType: "json", // 리턴받을 데이터의 타입 - text, xml 등...
data: { // 파라미터
},
success: function(data) { // 성공했을 때의 처리 콜백함수
$("#result").append("success<br />");
$(data.photos.photo).each(function() {
// 박스 만들기
var html = "<div style='float:left;margin:0 10px 10px 0;'><img src='http://farm"+this.farm+".static.flickr.com/"+this.server+"/"+this.id+"_"+this.secret+"_s.jpg' /></div>";
$("#list").append(html);
// 박스에 클릭이벤트 정의
$("#list").children(":last-child").click(function() {
// 페이드 아웃 효과 후 삭제
$(this).fadeOut(function() {
$(this).remove();
});
});
});
},
complete: function() { // ajax 전송이 완료 됐을 때의 콜백함수
stopLoading();
$("#result").append("complete<br />");
},
error: function() { // 에러가 발생했을 때의 콜백함수
$("#result").append("error<br />");
}
});
</script>
setTimeout 메소드는 지정된 시간 이후에 함수를 실행한다.
clearTimeout 메소드는 setTimeout을 시켰을 경우 타이머가 동작하게 되는데 이 타이머를 해제하는 역할을 한다.
setTimeout 메소드는 지정된 시간 이후에 단 한 번만 함수를 실행시키는 반면에 setInterval 메소드는 지정된 시간마다 함수를 실행한다.
clearTimeout 메소드는 setInterval 메소드로 타이머를 동작시켰을 때 해제하는 역할을 한다.
<div id="clock" style="margin-bottom:10px;"></div>
<button id="stopClock">시간아 멈춰라!</button>
<hr style="margin:10px 0 10px 0;" />
<div id="fivesec" style="display:none;border:1px solid #000;">5초 후에 보여주는 레이어</div>
<button id="dontdisplay">안봐!</button>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
// 5초 후에 5초 레이어 보여주기
var timer1 = setTimeout(function() {
// 팝업 보여주기
$("#fivesec").show();
},5000);
// 5초 팝업 타이머 해제하는 액션
$("#dontdisplay").click(function() {
clearTimeout(timer1);
timer1 = null;
});
// ---------------------------
// 시계
var timer2 = setInterval(function() {
// 현재시간 불러와서 변수에 저장
var d = new Date();
// 현재시간 출력
$("#clock").html(d.toString());
},1000);
// 시간 멈추는 액션
$("#stopClock").click(function() {
// 타이머 해제
clearInterval(timer2);
timer2 = null;
});
</script>
자바스크립트에서는 날짜에 관한 기본적인 것들은 Date 객체가 담당한다. 예를 들면 현재 시간을 가져오거나, 1월 1일이 무슨 요일인지 등을 알 수 있게 돕는 객체다.
아래 링크에서 Date 객체의 모든 것을 확인 할 수 있다.
http://w3schools.com/jsref/jsref_obj_date.asp
<script>
// ---- 함수 정의부
// 타이머 변수 생성 - 전역
var timer_start = null;
var timer_end = null;
// 시작 시각 구하기 함수
function startTimer() {
timer_start = new Date();
}
// 정지 시간 구하기 함수
function stopTimer() {
timer_end = new Date();
}
// 시간 계산하기
function getTimer() {
var result = timer_end.getTime()-timer_start.getTime();
console.log(result+"ms");
}
// ---- 구현부
startTimer(); // 시작 시각 구하기 함수 실행
for(var i=0;i<100000000;i++) {
var a = 1;
}
stopTimer(); // 정지 시간 구하기 함수 실행
getTimer(); // 시간 계산해서 콘솔로 출력
</script>