2018년 6월 30일 토요일

Wildcard SSL 설정

목표

  • letsencrypt wildcard SSL 설정

환경

  • CentOS7
  • Nnginx 1.13.8
  • DigitalOcean Droplet

예상 결과

  • www.{domain}.com 및 {domain}.com 호출시 SSL 접속


Certbot으로 인증서 발급

command

sudo certbot certonly \
--server https://acme-v02.api.letsencrypt.org/directory \
--manual \
--preferred-challenges dns \
-d *.{domain}.com \
-d {domain}.com \

  • wildcard 도메인 뿐 아니라 서브도메인이 없는 경우가 필요하다면 함께 추가

Issuing

Please deploy a DNS TXT record under the name
_acme-challenge.{domain}.com with the following value:

{value}

Before continuing, verify the record is deployed.

  • 위 값을 _acme-challenge 도메인의 DNS TXT 타입으로 등록
  • DNS record가 등록되어야만 다음단계 진행 가능
    • DNS Record 등록 부분 확인

성공시

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/{domain}.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/{domain}.com/privkey.pem
   Your cert will expire on 2018-09-28. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le


DNS Record 등록

  • TXT
    • _acme-challenge.thebudding.com => 위에서 제시한 값
    • 확인방법
      • nslookup -q=TXT {domain}
  • CNAME
    • wildcard 도메인을 redirect 하기 위함
    • *.{domain}.com => {domain}.com
  • NS / A


Nginx 설정

  • server {
        listen       80;
        server_name  ~.;
        return 301 https://$host$request_uri;
    }
    
    server {
        server_name  ~.;
        listen 443;
        
        ssl                  on;
        ssl_certificate      /etc/letsencrypt/live/{domain}/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/{domain}/privkey.pem;
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+ED';
        ssl_prefer_server_ciphers   on;
    
        location / {
         root   html;
         index  index.html index.htm;
      }
    }

참고자료

  • https://www.netmanias.com/ko/post/blog/5365/dns-network-protocol/three-types-of-dns-message-a-ns-and-cname
  • https://levelup.gitconnected.com/how-to-get-certbot-wildcard-certificates-3d25618a81e0
  • https://www.lesstif.com/pages/viewpage.action?pageId=27984443
  • https://certbot.eff.org/lets-encrypt/centosrhel7-nginx
  • https://blog.perfectacle.com/2017/10/05/letsencrypt-with-certbot-feat-aws/




2018년 2월 14일 수요일

Vue js 스터디

목표


  • vue js 로 프로젝트를 할 수 있을 정도 지식 획득
  • 스터디 책: vue.js 이정도는 알아야지 / 김지환.이선협 공저 / 비제이퍼블릭
  • webstorm

기반 프로젝트 생성

  • 참고
    • https://medium.com/codingthesmartway-com-blog/vue-js-2-quickstart-tutorial-2017-246195cfbdd2
    • https://medium.com/witinweb/vue-cli-%EB%A1%9C-vue-js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-browserify-webpack-22582202cd52
    • https://kr.vuejs.org/v2/guide/installation.html
  • Command
    • npm install -g vue-cli
    • vue init webpack vueapp01
    • cd vueapp01
    • npm install
    • npm run dev
    • npm run build


2018년 1월 27일 토요일

Server Setting

목표

  • 서버 설정방법을 자꾸 잊어버려 기록을 위해 정리 시작
  • 목표를 세분화하고 목적을 해결하는 방식으로 진행
  • 명령형으로 작성하고 내용을 시간순서로 추가하되 되도록 수정하지 않음
    • 결과가 잘못될 수 있으니, 꼭 마지막을 확인 바람

환경

  • DigitalOcean droplet (2 vCPUs, 4GB, 80GB ssd, $20/mo)
  • CentOS 7.4 x64

설정 목표

  1. admin, adminsu 등록
  2. nginx 컴파일
  3. jenkins 설치
  4. gitlab gitpush 등록
  5. /resource path에 static 연동
  6. static build ci 적용
  7. /index.html에 hello world 노출

서버 생성

  • 2 vCPUs, 4GB, 80GB ssd, $20/mo, Singapore
  • CentOS 7.4 x64
  • root 로그인 (pw 메일로 전송받음) 후 비밀번호 변경
  • sudo yum update -y
    • prm, yum 차이
      • http://blackub.tistory.com/15

CentOS7

  • systemd 이해
    • http://linux.systemv.pe.kr/centos-7-systemd-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0/
  • centos6와 차이
    • http://signpen.kr/sp_view.php?cat=D02&fid=155&sk=LHDS

admin, adminsu 계정 등록

  • 목적
    • admin은 일반 사용계정
    • adminsu는 sudo 권한 제공
  • 참고
    • http://kit2013.tistory.com/187
    • http://blog.freezner.com/archives/1094
      • adduser, useradd 차이는 debian 계열 linux만 차이가 있음
  • adduser admin, passwd admin
    • 일반사용자 su 권한 제한
      • http://klero.tistory.com/entry/root-%EA%B3%84%EC%A0%95-su-%EC%A0%9C%ED%95%9C
    • vi /etc/pam.d/su
  • adduser adminsu, passwd adminsu
    • su사용자 sudoer 권한 제공
      • https://slobell.com/blogs/40
    • usermod -aG wheel adminsu 
      • adminsu에 wheel 그룹 추가
    • /etc/sudoers 
      • ## Same thing without a password 
        %wheel ALL=(ALL) NOPASSWD: ALL  // 주석제거

Nginx 컴파일

  • 참고
    • https://www.vultr.com/docs/how-to-compile-nginx-from-source-on-centos-7
  • 준비, 추가패키지, 의존 패키지 설치
    • sudo yum groupinstall -y 'Development Tools'
    • sudo yum install -y vim
    • sudo yum install -y epel-release
    • sudo yum install -y wget
  • nginx 다운 및 압축해제
    • wget http://nginx.org/download/nginx-1.13.8.tar.gz
    • tar -zxvf nginx-1.13.8.tar.gz
  • nginx package 다운
    • pcre
      • wget https://ftp.pcre.org/pub/pcre/pcre-8.41.tar.gz
      • tar xzvf pcre-8.41.tar.gz
    • zlib
      • wget https://www.zlib.net/zlib-1.2.11.tar.gz
      • tar xzvf zlib-1.2.11.tar.gz
    • openssl
      • wget https://www.openssl.org/source/openssl-1.1.0g.tar.gz
      • tar xzvf openssl-1.1.0g.tar.gz
  • nginx configuration
    • ./configure --prefix=/etc/nginx \
                  --sbin-path=/usr/sbin/nginx \
                  --modules-path=/usr/lib64/nginx/modules \
                  --conf-path=/etc/nginx/nginx.conf \
                  --error-log-path=/var/log/nginx/error.log \
                  --pid-path=/var/run/nginx.pid \
                  --lock-path=/var/run/nginx.lock \
                  --user=nginx \
                  --group=nginx \
                  --build=CentOS \
                  --builddir=nginx-1.13.8 \
                  --with-select_module \
                  --with-poll_module \
                  --with-threads \
                  --with-file-aio \
                  --with-http_ssl_module \
                  --with-http_v2_module \
                  --with-http_realip_module \
                  --with-http_addition_module \
                  --with-http_xslt_module=dynamic \
                  --with-http_image_filter_module=dynamic \
                  --with-http_geoip_module=dynamic \
                  --with-http_sub_module \
                  --with-http_dav_module \
                  --with-http_flv_module \
                  --with-http_mp4_module \
                  --with-http_gunzip_module \
                  --with-http_gzip_static_module \
                  --with-http_auth_request_module \
                  --with-http_random_index_module \
                  --with-http_secure_link_module \
                  --with-http_degradation_module \
                  --with-http_slice_module \
                  --with-http_stub_status_module \
                  --http-log-path=/var/log/nginx/access.log \
                  --http-client-body-temp-path=/var/cache/nginx/client_temp \
                  --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
                  --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
                  --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
                  --http-scgi-temp-path=/var/cache/nginx/scgi_temp \
                  --with-mail=dynamic \
                  --with-mail_ssl_module \
                  --with-stream=dynamic \
                  --with-stream_ssl_module \
                  --with-stream_realip_module \
                  --with-stream_geoip_module=dynamic \
                  --with-stream_ssl_preread_module \
                  --with-compat \
                  --with-pcre=../packages/pcre-8.41 \
                  --with-pcre-jit \
                  --with-zlib=../packages/zlib-1.2.11 \
                  --with-openssl=../packages/openssl-1.1.0g \
                  --with-openssl-opt=no-nextprotoneg \
                  --with-debug
    • nginx modules 링크
      • sudo ln -s /usr/lib64/nginx/modules /etc/nginx/modules
    • nginx system 유저 및 그룹 등록
      • sudo useradd --system --home /var/cache/nginx --shell /sbin/nologin --comment "nginx user" --user-group nginx
    • nginx-t
      • 실패시 sudo mkdir -p /var/cache/nginx 생성후 다시 테스트
    • nginx server 시작 및 활성화
      • sudo systemctl start nginx.service
      • sudo systemctl enable nginx.service
      • sudo systemctl is-enabled nginx.service
      • sudo systemctl status nginx.service
    • 초기 파일 삭제
      • sudo rm /etc/nginx/koi-utf /etc/nginx/koi-win /etc/nginx/win-utf
    • nginx syntax highlighting
      • mkdir ~/.vim/
      • sudo cp -r ~/nginx-1.13.8/contrib/vim/* ~/.vim/
      • (cp -R /home/admin/apps/nginx-1.13.8/contrib/vim/* /home/adminsu/.vim/)

Jenkins 설치

  • JDK 설치가 먼저 필요하단걸 깜박함-_-

JDK 설치

  • 참조
    • https://tecadmin.net/install-java-8-on-centos-rhel-and-fedora/#
  • JDK 다운
    • wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u161-b12/2f38c3b165be4555a1fa6e98c45e0808/jdk-8u161-linux-x64.tar.gz"
  • /opt 로 이동 후 압축하제
    • cd /opt
    • tar xzf jdk-8u161-linux-x64.tar.gz
  • alternatives 명령어
    • https://skyoo2003.github.io/post/2017/03/17/what-is-alternatives-command
    • https://donghwi-kim.github.io/jekyll/update/2015/04/17/update-alternatives.html
  • alternatives 명령어로 심볼릭 링크 생성
    • alternatives --install /usr/bin/java java /opt/jdk1.8.0_161/bin/java 2
    • alternatives --config java
    • alternatives --install /usr/bin/jar jar /opt/jdk1.8.0_161/bin/jar 2
    • alternatives --install /usr/bin/javac javac /opt/jdk1.8.0_161/bin/javac 2
    • alternatives --set jar /opt/jdk1.8.0_161/bin/jar
    • alternatives --set javac /opt/jdk1.8.0_161/bin/javac
  • 확인
    • java -version

Tomcat 멀티 인스턴스 설치

  • 멀티 인스턴스를 시도하는 특별한 이유는 없음
    • 그냥, 예전 기억을 되살리기 위해 시도함
  • Jenkins용 서버와 SpringBatch용 서버로 활용할 예정
    • 추후 서비스가 커지는 경우 외부 장비로 이동할 예정 (그럴일이 없을수도 있음)
  • 참고
    • https://cocagolau.blogspot.kr/2014/03/blog-post_4571.html
  • 하려다가, 뭐 귀찮게 할 필요가 있나란 생각이 들어 포기함

Tomcat 설치

  • 톰켓8을 다운받으려 했으나, 9.0.4 버전이 나옴. 잠시 고민했지만, 8버전을 다운받기로 함
  • 참고
    • https://gs.saro.me/#!m=elec&jn=768
  • 다운
    • wget http://apache.tt.co.kr/tomcat/tomcat-8/v8.5.27/bin/apache-tomcat-8.5.27.tar.gz
  • 설정 변경
    • 포트번호 수정, ajp 포트 주석처리
  • 서버 실행 후 서버 접근
    • 방화벽에 막혀 있으므로 해당포트 방화벽 제거 (digitalocean)
    • 그래도 ERR_CONNECTION_REFUSED 발생
  • SeLinux
    • https://ko.wikipedia.org/wiki/%EB%B3%B4%EC%95%88_%EA%B0%95%ED%99%94_%EB%A6%AC%EB%88%85%EC%8A%A4
    • https://lesstif.gitbooks.io/web-service-hardening/content/selinux.html
    • http://navs.tistory.com/entry/SELinux-%EC%99%80-httpd-%ED%8F%AC%ED%8A%B8-%EB%B0%94%EC%9D%B8%EB%94%A9-%ED%95%98%EA%B8%B0
  • 설정된 포트를 SeLinux에 추가
    • semanage port -l | grep http
    • semanage port -a -t http_port_t -p tcp 포트번호
  • .. 서버는 뜨지만, 접근이 안됨. 이유를 모르겠음. 좀더 찾아보겠음 바로 다시 됨. 이놈 세계는 알수 없다.
  • Tomcat context 설정
    • https://ohjongsung.io/2017/06/24/tomcat-8%EC%97%90-root%EB%A1%9C-%EA%B2%8C%EC%8B%9C%ED%95%98%EA%B8%B0

Jenkins 설치

  • 다운로드
    • wget http://mirrors.jenkins.io/war/2.103/jenkins.war
  • 톰켓 배포 후 시작
  • 설정에서 역방향 프록시 설정이 잘못되었다고 나올 때
    • http://www.comsvc.kr/xe/comsvc_tip02/3846
    • sudo getsebool -a
    • sudo setsebool -P httpd_can_network_connect on
    • https://www.lesstif.com/pages/viewpage.action?pageId=22053077
    • '해제' 버튼을 눌러서 끝냄.

Gitlab push event 적용

  • 참고
    • http://yg-park.github.io/2015/03/10/gitlab-jenkins/
    • https://github.com/jenkinsci/gitlab-plugin/issues/375
  • 설치목록
    • Credentials Plugin, GIT plugin, GitLab Plugin, embeddable-build-status
  • 내용
    • jenkins 설정에서 secret key 꼭 설정

Nginx 와 Jenkins 연동


  • 목표
    • jenkins 의 endpoint를 nginx를 사용하도록 수정
  • 참조
    • https://nginx.org/en/docs/
    • https://www.nginx.com/resources/wiki/start/topics/examples/full/
    • https://www.digitalocean.com/community/tutorials/understanding-the-nginx-configuration-file-structure-and-configuration-contexts
    • https://blog.lael.be/demo-generator/nginx/default.conf.php
    • http://nginx.org/en/docs/example.html
    • https://www.digitalocean.com/community/tutorials/understanding-nginx-http-proxying-load-balancing-buffering-and-caching
    • http://nginx.org/en/docs/http/server_names.html
    • http://nginx.org/en/docs/http/request_processing.html
    • http://sarc.io/index.php/nginx/61-nginx-nginx-conf
    • https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

static build ci 적용

  • 목포
    • jenkins 2.0 파이프라인 기능을 사용하여 배포를 자동화 설정
  • 참고
    • http://kingbbode.tistory.com/42
    • https://jenkins.io/doc/book/pipeline/getting-started/
    • https://jenkins.io/doc/book/pipeline/syntax/

Index.html을 root로 변경

  • 참고
    • https://www.nginx.com/resources/admin-guide/serving-static-content/
  • Nginx conf중 user
    • https://opentutorials.org/module/384/4530
    • 마스터 프로세스는 root로
    • conf의 user는 워커 프로세스의 실행권한

2014년 10월 23일 목요일

jenkins에서 tomcat 실행이 안되는 문제
 - http://lng1982.tistory.com/178
 - https://wiki.jenkins-ci.org/display/JENKINS/ProcessTreeKiller

2014년 4월 23일 수요일

[SQL] prepared statement에서 LIKE 사용방법


0. 환경1

macbook-pro 13” 2012 mid / 10GB
parallels9 ubuntu 12.04 LTS


1. 문제

preparedStatement를 사용시 SQL QUERY에 LIKE 문법 있을 때 처리방법

코드

StringBuffer query = new StringBuffer();
query.append("SELECT * FROM article WHERE name LIKE '%?%'");

// logger
logger.debug("name: " + name);

psmt = conn.prepareStatement(query.toString());
psmt.setString(1, name);  // 이 부분에서 에러 발생
rs = psmt.executeQuery();

에러 메시지

12:00:25.498 [DEBUG] [http-bio-8080-exec-3] [next.wildgoose.dao.ReporterCardDAO] - name: 
12:00:25.514 [DEBUG] [http-bio-8080-exec-3] [next.wildgoose.dao.ReporterCardDAO] - Parameter index out of range (1 > number of parameters, which is 0).
java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0).
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1086) ~[mysql-connector-java-5.1.29.jar:na]
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989) ~[mysql-connector-java-5.1.29.jar:na]
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975) ~[mysql-connector-java-5.1.29.jar:na]
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:920) ~[mysql-connector-java-5.1.29.jar:na]
    at com.mysql.jdbc.PreparedStatement.checkBounds(PreparedStatement.java:3796) ~[mysql-connector-java-5.1.29.jar:na]
    at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:3778) ~[mysql-connector-java-5.1.29.jar:na]
    at com.mysql.jdbc.PreparedStatement.setString(PreparedStatement.java:4599) ~[mysql-connector-java-5.1.29.jar:na]
    at next.wildgoose.dao.ReporterCardDAO.findReportersByName(ReporterCardDAO.java:101) ~[ReporterCardDAO.class:na]

2. 해결방법

Mysql

StringBuffer query = new StringBuffer();
query.append("SELECT * FROM article WHERE name LIKE ?");

psmt.setString(1, "%"+name+"%"); 

Oracle

StringBuffer query = new StringBuffer();
query.append("SELECT * FROM article WHERE name LIKE '%' || ? || '%'");

psmt.setString(1, name); 

Note
Oracle의 방법을 Mysql에 적용할 경우,
첫 번째 요청에 대해서는 결과를 응답하지만
name을 바꾼 후 요청은 update시키지 못한다는 문제가 발생했다.
그 이유에 대해서는 아직 찾아보지 못했다.


* 참고자료

stackoverflow - 2857164
stackoverflow - 8247970


Written with Dec7.

2014년 4월 14일 월요일

[javascript] forWD - 4장 변수, 스코프, 메모리


0. 환경

macbook-pro 13” 2012 mid / 10GB


1. 앞장 정리

변수

  1. 자바 스트립트의 변수는 느슨한 타입

    느슨한 타입

    • 한 변수에 어떤 타입이라도 저장할 수 있음
  2. 초기화 하지 않을 경우 undefined

데이터타입

  1. 원시 데이터 타입 (다섯가지의 기본 데이터 타입)
    1. undefined
    2. null
    3. boolean
    4. 숫자
    5. 문자열
  2. 객체 (복잡한 데이터 타입)
    • key - value 쌍의 순서없는 형태

자바 스크립트 데이터 타입은 동적이라서 한 가지 데이터 타입이 여러 특성을 가질 수 있음음

typeof 연산자

자바스크립트의 변수는 느슨한 타입이므로 변수의 타입을 알아야 할 때
typeof는 원시 값에 대해서는 잘 작동하지만 참조 값에서는 쓸모 없음

타입 내용
undefined 정의되지 않은 변수
boolean 불리언
string 문자열
number 숫자
object 함수를 제외한 객체 또는 null
function 함수

instanceof 연산자

// 문법
'result' = 'variable' instanceof 'constructor'

변수가 주어진 참조타입의 인스턴스일 때 true반환

  1. 인스턴스는 프로토타입 체인으로 판단함
  2. 모든 참조 값은 Object의 인스턴스

Note

  1. typeof연산자를 함수에 사용시 function 반환
  2. typeof연산자를 정규표현식에 사용시 function 반환
    • ECMA-262에서 내부에 [call]메소드를 가진 객체는 typeof에서 function를 반환해야한다고 명시했기 때문
      - 사파리5까지, 크롬7까지만 적용, firefox, IE는 object 반환

2. 변수

  1. 원시 데이터 타입
    • 변수에 저장된 실제 값을 조작
  2. 객체 타입
    • 참조 값은 메모리에 저장된 객체
    • 객체의 메모리 공간을 직접 조작할 수 없으므로 참조로 접근

동적 프로퍼티

참조 값의 경우 언제든 프로퍼티와 메소드를 추가/삭제 가능
원시 값의 경우 프로퍼티가 없지만 추가하려도 해도 오류가 발생하지는 않음

// 참조 값
var person = new Object();
person.name = "Nicholas";
console.log("person.name: " + person.name);
console.log("person.age: " + person.age);
person.age = 30;
console.log("person.age: " + person.age);

console.log("------------\n");

// 원시 값
var name = "Dec7";
console.log("name: " + name);
console.log("name.age: " + name.age);
name.age = 30;
console.log("name.age: " + name.age);

/*
* 출력 결과
**************
person.name: Nicholas
person.age: undefined
person.age: 30
------------

name: Dec7
name.age: undefined
name.age: undefined 
**************
*/

값 복사

// 원시 값
var num1 = 5
var num2 = num1

num2의 값은 복사되었고, num1의 값과 완전히 분리되어 있음
원시값 복사

// 참조 값
var obj1 = new Object();
var obj2 = obj1;

obj1.name = "Dec7";
console.log("obj2.name: " + obj2.name);

/*
* 출력결과
**************
dog.name: Dec7
**************
*/

참조 값의 경우 다른 변수에 복사할 객체 자체가 아닌 heap에 저장된 객체를 가리키는 포인터를 복사함.
복사 후 두 변수는 동일한 객체를 가리킴.
참조값복사

매개변수

원시값이든 참조값이든 모두 오직 값으로 전달.
매개변수를 값으로 전달하면 지역 변수에 저장됨.
- arguments 객체로 저장
매개변수를 참조형으로 전달하면 메모리상 주소 위치가 지역변수에 저장. 지역변수의 내용을 변경하면 함수 외부에도 반영됨.

// 매개변수가 원시 값일 때
function add(num){
    num += 10;
    return num;
}

var count = 20;
var result = add(count);
console.log("count: " + count);
console.log("result: " + result);

/*
* 출력 결과
**************
count: 20
result: 30 
**************
*/
// 매개변수가 참조 값일 때
function setName (obj) {
    obj.name = "Dec7";
    /*
    * new object
    * obj가 값이 아닌 참조 형태로 전달된다면
    * 출력결과는 Galaxy가 되어야 함
    */
    obj = new Object();
    obj.name = "Galaxy";
    /*
    * 함수 내부에서 위 obj를 덮어쓰면
    * obj는 지역 객체를 가리키는 포인터가 되고
    * 이 지역객체는 함수가 종료되는 즉시 파괴됨.
    */
}

var person = new Object();
setName(person);
console.log("person.name: " + person.name);

/*
* 출력 결과
**************
person.name: Dec7
**************
*/

함수 외부의 person이든 함수 내부의 obj모두 동일한 메모리 주소를 가리킴.
obj가 가리키는 것은 heap에 존재하는 전역객체이기 때문


3. 실행 컨택스트와 스코프

실행 컨텍스트

  • 이름
    • (변수/함수) 실행 컨텍스트(EC)
    • 짧게 컨텍스트라고 부름
  • 하는 일
    • 다른 데이터에 접근할 수 있는지 규정
    • 어떻게 행동하는지를 규정
  • 특징

    • 각 실행 컨텍스트에는 변수객체(VO)가 연결됨
    • 해당 컨텍스트에 정의된 모든 변수와 함수는 이 객체에 존재
    • 이 객체를 코드에서 직접 접근 불가
    • 이면에서 데이터를 다룰 때 이 객체를 사용
    • 실행 컨텍스트는 그것이 포함된 코드가 모두 실행되면 파괴됨
  • 종류

    1. 전역 컨텍스트

      • 가장 바깥쪽에 존재하는 실행 컨텍스트
      • 웹 브라우저에서 window라고 부름
      • 전역변수와 함수는 window객체의 property, method로 생성
      • 웹 브러우저가 닫칙 때까지 유지됨
    2. 함수 컨텍스트

      • 독자적인 실행 컨텍스트
      • 함수 호출시 생성됨
      • 실행 순서
        1. 실행시
          • 함수의 컨텍스트가 컨텍스트 스택쌓임.
        2. 종료시
          • 해당 컨텍스트를 컨텍스트 스택에서 꺼냄.
          • 컨트롤을 이전 컨텍스트로 인전
    3. eval()을 호출할 때 생성되는 세 번재 타입이 있긴함 (p117)

스코프체인

  • 컨텍스트에서 코드를 실행하면 변수객체에 스코프 체인이 형성
  • 목적
    • 실행 컨텍스트가 접근할 수 있는 모든 변수, 함수에 순서 정의

스코프체인 실행순서

  • 스코프 체인의 앞쪽
    • 항상 코드가 실행되는 컨텍스트의 변수 객체
    • 컨텍스트가 함수시 활성화 객체를 변수 객체로 사용

      활성화 객체

      • 항상 arguments 변수 하나로 시작
      • arguments객체는 전역 컨텍스트에는 없음
  • 변수객체 다음
    • 해당 컨텍스트를 포함하는 부모 컨텍스트
    • 그 다음은 부모의 부모 컨텍스트
    • 전역 컨텍스트까지 도달
  • 스코프 체인의 마지막
    • 전역 컨텍스트의 변수 객체

식별자를 찾기 위해 스코프 체인 순서를 따라가며 검색
(없는 경우, 에러 발생)

스코프체인 예제

var color = "blue";

function changeColor(){
    var anotherColor = "red";

    function swapColors(){
        var tempColor =anotherColor;
        anotherColor = color;
        color = tempColor;
        // color,anotherColor,tempColor모두 접근가능
    }
    // color,anotherColor접근가능.tempColor는 불가능
    swapColors();
    }
// color만접근가능
changeColor ( ) ;
  • 존재하는 실행 컨텍스트

    1. 전역 컨텍스트

      1. color변수 + changeColor()함수
    2. changeColor()의 지역 컨텍스트

      1. anotherColor변수 + swapColors()함수
      2. 전역 컨텍스트
    3. swapColors()의 지역 컨텍스트

      1. tempColor변수
      2. changeColor()함수의 컨텍스트
      3. 전역 컨텍스트
    4. 그림

      실행컨텍스트

  • 특징

    • 내부 컨텍스트는 스코프 체인을 통해 외부 컨텍스트로 접근 가능
    • 외부 컨텍스트는 내부 컨텍스트를 전혀 알 수 없음
  • swapColors()의 로컬 컨텍스트

    • 변수 객체 3개를 가짐

      1. swapColors()의 변수객체
      2. changeColor()의 변수객체
      3. 전역 변수객체
    • 자신의 변수 객체에서 변수나 함수를 찾음

    • 찾지 못할 경우 스코프 체인을 따라 한 단계씩 올라감
    • 아래로는 내려갈 수 없음
    • 함수 매개변수도 변수로 간주되어 실행 컨텍스트의 변수와 같은 규칙

스코프체인 확장

try-catch문의 catch블록with문
- 스코프체인 앞부분에 임시로 변수객체 생성
- 생성된 객체는 코드 실행이 끝나면 사라짐

  • with문

    해당 객체가 스코프체인에 추가

  • catch블록

    에러 객체를 선언하는 변수 객체가 생성

function buildUrl() {
    var qs = "?debug=true";

    /*
    * with문이 location객체에 적용되므로
    * location 객체개 scope chain에 추가됨
    */
    with(location) {
        /*
        * href는 location.href를 참조
        * qa는 buildUrl()함수 컨텍스트의 변수객체 참조
        * 
        * with문 내에서 선언한 url은 
        * buildUrl() 함수 컨텍스트로 편입됨
        */
        var url = href + qs;
    }
    /*
    *  with문을 통해 편입되었기 때문에 반환할 수 있음
    */
    return url;
}

var result = buildUrl();
console.log("url: " + result);

/* 
 * 실행결과
*************************
url: file:///Users/Dec7/Desktop/Javascript.html?debug=true
*************************
*/

Note

  • IE8까지는 JS구현에 문제가 있음
    • catch문에서 잡아낸 에러가 catch문의 변수객체가 아닌 실행 컨텍스트의 변수객체로 추가되어 catch블록 외부에서 접근 가능한 버그가 있음
  • IE9부터는 수정됨

블록레벨 스코프

for (var i=0; i < 10; i++){
    // do-something
}
alert(i); // 10
  • JS는 블록단위 스코프가 없음
  • JS는 변수를 선언할 때 현재 실행 컨텍스트에 추가

변수선언

  • var를 사용해 선언하면 자동으로 가장 가까운 컨텍스트에 추가
  • var를 사용하지 않으면 자동으로 전역 컨텍스트에 추가
// 전역 컨텍스트 예제
function one() {
    var one_num = 10;

    function two() {
        var two_num = 20;

        function three () {
            var three_num = 30;

            function four() {
                // var 키워드 미사용, 전역선언
                four_num = 40;
                console.log("in four(), four_num: " + four_num);
            }
            four();
            console.log("in three(), four_num: " + four_num);
        }
        three();
        console.log("in two(), four_num: " + four_num);

    }
    two();
    console.log("in one(), four_num: " + four_num);
}

one();
console.log("in global, four_num: " + four_num);

/*
* 출력결과
*********************
in four(), four_num: 40
in three(), four_num: 40
in two(), four_num: 40
in one(), four_num: 40
in global, four_num: 40 
*********************
*/
// var 키워드를 사용한 경우
function one() {
    var one_num = 10;

    function two() {
        var two_num = 20;

        function three () {
            var three_num = 30;

            function four() {
                // var 키워드 사용
                var four_num = 40;
                console.log("in four(), four_num: " + four_num);
            }
            four();
            console.log("in three(), four_num: " + four_num);
        }
        three();
        console.log("in two(), four_num: " + four_num);

    }
    two();
    console.log("in one(), four_num: " + four_num);
}

one();
console.log("in global, four_num: " + four_num);

/*
* 출력결과
*********************
in four(), four_num: 40
Uncaught ReferenceError: four_num is not defined
// console.log("in three(), four_num: " + four_num);에서 에러발생
*********************
*/

Note
- 변수를 선언하지 않고 사용하면 하면 에러 가능성 높아짐
- 스트릭모드에서 변수를 선언하지 않고 초기화시 에러

식별자검색

  • 컨텍스트 내부에서 식별자를 참조하기 전에 검색이 선행
  • 검색은 스코프체인 앞에서 시작
  • 식별자 이름으로 검색
  • 로컬 컨텍스트에서 검색 후 찾지 못하면
    • 스코프체인을 따라 검색을 계속함
    • 스코프체인 내부의 객체에도 프로토타입 체인이 있으므로 각 객체의 프로토타입 체인을 따라 검색할 수도 있음
  • 같은 식별자가 로컬 컨텍스트에 정의되어 있으며 부모 컨텍스트에 동일한 식별자가 있다고 하더라도 참조 불가
  • 검색에도 비용이 발생
    • 지역변수는 스코프체인 검색이 필요없으므로 전역변수보다 빨리 검색

4. 가비지 콜렉션

  • 더 이상 사용하지 않는 변수를 찾고 메모리를 회수함
  • 이 프로세스는 주기적으로 실행됨
  • [비추] 코드 실행 중 특정 시점에서 메모리 회수하도록 지정할 수도 있음
  • -

변수 사용여부


  • 더 이상 변수가
  • 하지만 명확하지 않은 경우가 많음
    • 사용여부를 확인 후 회수
    • 사용될 가능성이 있는지를 추측
    • 더 이상 사용하지 않는 변수의 기준을 브라우저마다 다름

      지역 변수
      함수를 실행하면 지역 변수가 생성됨
      함수가 종료된다면 그 지역 변수는 더 이상 필요 없음

* 참고자료


Written with Dec7.