Author Archives: 아이

macOS Mojave 에서 SSL 지원하는 Apache 웹서버 설치하기

이번에 새롭게 출시된 macOS 모하비에서 Apache를 설치하는 방법을 정리 해 보겠습니다. 또한 추가로 HTTPS 프로토콜을 지원하는 설정도 함께 적어보도록 하겠습니다. 이글은 macOS에서 Homebrew를 사용중인것을 가정하고 작성되었습니다.

과거 시에라등의 버전에서는 Apache 웹서버가 기본적으로 설치가 되어있습니다. 계속해서 업데이트를 해왔다면 해당 아파치 웹서버가 그대로 살아있을수 있으니 다음과 같은 방법으로 제거를 해줍시다.

$ ps aux | grep httpd
_www               232   0.0  0.0  4329504    936   ??  S    12:12AM   0:00.00 /usr/local/opt/httpd/bin/httpd -D FOREGROUND
_www               231   0.0  0.0  4321312    908   ??  S    12:12AM   0:00.00 /usr/local/opt/httpd/bin/httpd -D FOREGROUND
_www               230   0.0  0.0  4329504    888   ??  S    12:12AM   0:00.03 /usr/local/opt/httpd/bin/httpd -D FOREGROUND
_www               229   0.0  0.0  4321312    932   ??  S    12:12AM   0:00.00 /usr/local/opt/httpd/bin/httpd -D FOREGROUND
_www               228   0.0  0.0  4329504    924   ??  S    12:12AM   0:00.00 /usr/local/opt/httpd/bin/httpd -D FOREGROUND

위와 같이 돌아가고 있는 httpd 프로세스가 있는것을 확인하였다면 다음의 명령을 사용하여 종료하고 기본 구동 서비스에서도 삭제 해 줍니다.

$ sudo apachectl stop
$ sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist

위와 같은 명령을 사용하고 기본적으로 설치되어있는 아파치 웹서버를 더이상 실행되지 않도록 변경할 수 있습니다. 이번에는 새로운 아파치 2.4 버전을 Homebrew를 사용하여 설치한 뒤 서비스로 구동하는 방법을 알아보겠습니다.

$ brew install httpd24 --with-privileged-ports --with-http2

위와 같은 명령을 사용하여 매우 간단하게 아파치 웹서버 2.4 버전을 설치할 수 있었습니다. –with-privileged-ports 옵션을 1024 이하의 낮은 숫자의 시스템 포트를 사용하기 위해서 사용하는 옵션입니다. 이 옵션을 사용하여 80 포트를 사용할 수 있게 되며 8080과 같은 포트를 사용하려면 굳이 이 옵션을 사용하실 필요가 없습니다.

이제 아파치의 설정 파일을 수정하여 80 포트로 구동되도록 수정하겠습니다.

$ vi /usr/local/etc/httpd/httpd.conf

다음의 내용을 찾아서

Listen 8080

다음과 같이 수정합니다.

Listen 80

이제 다음의 명령을 사용하여 웹서버를 구동할 수 있습니다.

$ sudo brew services start httpd24

위에서 설명했던것과 같이 1024 이하의 시스템 포트를 사용하려면 sudo를 사용하여 root 권한으로 구동해야 합니다. 이후의 포트를 사용하려면 굳이 sudo를 사용하지 않아도 사용이 가능합니다.

정상적으로 설치가 되었고 구동이 되었다면 웹브라우저를 통해서 localhost에 접속해 보면 “It works!”라는 문자열 출력을 확인하실 수 있습니다.

이번에는 SSL 설정을 해보도록 하겠습니다. 다시한번 아파치 웹서버 설정 파일을 수정 해 보겠습니다.

$ vi /usr/local/etc/httpd/httpd.conf

다음의 4가지 항목을 찾아서 모두 주석을 해제 해 줍니다.

#LoadModule socache_shmcb_module lib/httpd/modules/mod_socache_shmcb.so
#LoadModule ssl_module lib/httpd/modules/mod_ssl.so
#Include /usr/local/etc/httpd/extra/httpd-vhosts.conf
#Include /usr/local/etc/httpd/extra/httpd-ssl.conf

또한 ServerName 항목을 찾아서 주석을 해제하고 다음과 같이 내용을 수정 해 줍니다.

ServerName localhost:80

httpd.conf 파일의 수정은 완료되었고 이번에는 httpd-ssl.conf 파일을 수정 해 보겠습니다.

$ vi /usr/local/etc/httpd/extra/httpd-ssl.conf

다음의 내용을 찾아서

Listen 8443

다음과 같이 수정 해 줍니다.

Listen 443

추가로 좀 더 밑으로 가서 다음의 내용을 찾아서

<VirtualHost _default_:8443>

# General setup for the virtual host
DocumentRoot "/usr/local/var/www"
ServerName www.example.com:8443
ServerAdmin you@example.com

다음과 같이 수정 해 줍니다.

<VirtualHost _default_:443>

# General setup for the virtual host
#DocumentRoot "/usr/local/var/www"
#ServerName www.example.com:443
#ServerAdmin you@example.com

이번에는 httpd-vhosts.conf 파일을 수정하여 가상호스트 설정을 해 보겠습니다.

$ vi /usr/local/etc/httpd/extra/httpd-vhosts.conf

이미 몇개의 가상호스트 설정이 있습니다만, 모두 주석 처리 해 주고 다음의 두가지 설정을 추가 해 줍니다.

<VirtualHost *:80>
    DocumentRoot /usr/local/var/www
    ServerName localhost
</VirtualHost>

<VirtualHost *:443>
    DocumentRoot /usr/local/var/www
    ServerName localhost
    SSLEngine on
    SSLCertificateFile "/usr/local/etc/httpd/server.crt"
    SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
</VirtualHost>

여기서 눈여겨 봐야 하는 부분은 DocumentRoot 설정을 통해서 /usr/local/var/www를 웹서버의 기본 리소스 디렉토리로 설정했다는것과 443포트 연결에 대해서 SSLEngine이 on되었고 추가로 SSLCertificateFile와 SSLCertificateKeyFile 세팅이 되었다는 점 입니다.

DocumentRoot의 설정은 본인의 개발 환경에 맞게 설정 해 주시면 됩니다.

이번에는 로컬에서만 사용될 Self-Signed 인증서를 만들어보도록 하겠습니다. 여기서 만들 인증서 파일을 사용하겠다는 설정은 이미 위에서 해두었습니다. 다음의 명령어를 사용하여 인증서를 만드시면 됩니다.

$ cd /usr/local/etc/httpd
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt
Generating a 2048 bit RSA private key
.............+++
.......................................................................................+++
writing new private key to 'server.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:KR
State or Province Name (full name) []:Gyeonggi-do
Locality Name (eg, city) []:Seongnam-si
Organization Name (eg, company) []:MyCompany
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:
Email Address []:

이제 다 된것 같습니다. 다음의 명령을 사용하여 설정이 정상적으로 되었는지 확인이 가능합니다.

$ sudo apachectl configtest
Syntax OK

이제 웹브라우저를 재시행하여 http://localhost https://localhost 둘 모두 접속이 잘 되는것을 확인 해 봅시다.

$ sudo brew services restart httpd24

Javascript – Garbage Collection

자바스크립트의 메모리 관리는 우리에게는 보이지 않게 자동으로 실행됩니다. 우리가 원시타입의 변수나 혹은 객체, 함수를 선언할때도 모두 메모리를 사용합니다. 만약에 이러한 것들이 더이상 필요없게 된다면? 자바스크립트 엔진은 어떻게 이것들을 찾아내어 삭제할까요?

접근 가능성(Reachability)

자바스크립트 메모리 관리의 주요 개념은 접근 가능성입니다. 간단하게 말하면 “접근 가능한” 값은 어떻게든 엑세스가 가능하거나 사용할 수 있는 값임을 뜻합니다. 이들은 메모리에 유지되는것을 보장 받습니다.

  1. 명백한 이유로 삭제될 수 없는 본질적으로 값에 접근 가능한 기본 세트가 있습니다. 이런것들을 뿌리(Root)라고 부르겠습니다.
    • 현재 함수의 지역 변수와 매개변수
    • 다른 함수의 중첩 호출로 실행된 함수의 경우 현재의 스코프 체인으로 접근 가능한 변수와 매개변수
    • 전역 변수
    • (내부적으로 구현된 다른 것들도 있다;;)
  2. 기타 다른 값은 참조(Reference) 또는 레퍼런스의 참조(A chain of references)에 의해 루트에서 도달 가능한 것으로 간주합니다. 예를 들어 로컬 변수가 특정 객체를 참조하고 있고 그 객체가 또다른 객체를 참조하는 프로퍼티를 가지고 있다면 그 객체에 도달 가능하다라고 간주합니다. 그리고 그것들이 참조하는 다른 것들도 도달할 수 있습니다.

자바스크립트 엔진의 백그라운드 프로세스로 동작하는 가비지 콜렉터라는것이 있습니다. 이것은 모든 객체들을 모니터링 하며 그것들이 접근 불가능하게 되었을 때 삭제하는 작업을 수행합니다.

간단한 예제

여기에 아주 간단한 예제가 있습니다.

// user는 객체에 대한 참조를 가지고 있습니다.
let user = {
  name: "John"
};

여기에 표시된 화살표는 객체 참조를 나타냅니다. 전역 변수 “user”는 {name: “John”} 객체를 참조합니다. (여기서 우리는 짧게 존이라고 부르겠습니다). 존의 “name” 프로퍼티는 원시 타입을 저장하여 객체의 내부에 위치합니다. 여기에서 우리가 user의 값을 덮어쓰게 되면 참조를 잃게 됩니다.

user = null;

이제 존은 접근이 불가능하게 되었습니다. 여기에 접근할 방법은 없으며 아무도 존을 참조하지 않게 되었습니다. 가비지 콜렉터는 데이터를 회수하고 메모리를 비우게 됩니다.

두개의 참조

이번에는 user를 admin으로 참조를 복제하였다고 상상 해 보겠습니다.

let user = {
  name: "John"
};

let admin = user;

이제 우리는 똑같은 작업을 한번 더 해보겠습니다.

user = null;

하지만 여전히 admin 변수가 존을 참조하고 있으므로 메모리에 유지됩니다. 우리가 admin 도 다시 덮어쓴다면 그때 삭제 될 것입니다.

상호 연결된 객체

이번엔 좀 더 복잡한 예제를 살펴보겠습니다. 가족을 예로 들어보겠습니다.

function marry(man, woman) {
  woman.husband = man;
  man.wife = woman;

  return {
    father: man,
    mother: woman
  }
}

let family = marry({
  name: "John"
}, {
  name: "Ann"
});

marry는 두개의 객체를 서로 참조하게 하고 이 둘을 참조하고 있는 새로운 객체를 반환하는 “결혼”(?)을 시키는 함수입니다. 메모리 구조의 결과는 다음과 같습니다.

모든 객체는 서로 접근 가능하게 되었습니다. 이제 두개의 참조를 삭제해보겠습니다.

delete family.father;
delete family.mother.husband;

이 두개의 참조중에 하나만 삭제할 경우에는 여전히 모든 객체가 접근 가능하므로 객체가 삭제되기에 충분하지 않습니다. 하지만 둘 모두를 삭제할 경우 존을 참조하는 참조는 더이상 존재하지 않게 됩니다.

존이 여전히 가지고 있는 바깥으로 향하는 참조는 상관 없습니다. 오로지 안으로 들어오는 참조만이 존을 참조가능한 상태로 만들어줍니다. 그러므로 존은 이제 접근 불가능하게 되었고 메모리에서 제거되게 될 것입니다. 가비지 콜렉션이 동작한 이후에는 다음과 같이 됩니다.

접근 불가능한 섬

외부에서 접근 불가능한, 자기들끼리만 상호 참조하여 만들어진 완벽한 형태의 섬도 메모리에서 삭제 가능합니다. 소스코드는 위와 동일하다고 가정하고 다음의 코드를 실행하도록 하겠습니다.

이 예제는 접근 가능성에 대한 매우 중요한 개념을 보여주는 데모입니다. 명백하게 존과 앤은 연결되어있습니다. 그 둘은 안/밖으로 연결되는 링크들 모두를 가지고 있습니다. 하지만 이것만으로는 충분하지 않습니다. 이전의 family 객체는 루트(Root)와의 연결이 끊어지게 되었습니다. 그러므로 이 완벽한 섬은 접근 불가능하게 되었으며 삭제될 것입니다.

내부 알고리즘

기본적인 가비지 콜렉션의 알고리즘은 마크 앤 스윕(Mark-and-sweep) 이라고 불립니다. 일반적으로 가비지 콜렉션은 다음의 과정을 거칩니다.

  • 가비지 콜렉터는 루트를 획득하여 그들을 마크(기억)합니다.
  • 그리고 그들이 참조하고 있는 모든 것들에 방문하여 마크합니다.
  • 그리고 마크한 모든 객체에 방문하여 그들의 참조 역시 마크합니다. 모든 객체들을 기억하고 나면 미래에는 같은 객체를 두번 방문하지 않습니다.
  • 루트로부터 접근 가능한 방문하지 않은 참조가 있다면 계속해서 반복합니다.
  • 마크되지 않은 모든 객체는 삭제됩니다.

예를 들어 다음과 같은 객체의 구조가 있다고 해보겠습니다.

우리는 오른편에 “접근 불가능한 섬”을 발견할 수 있습니다. 이제 가비지 콜렉터가 진행하는 마크 앤 스윕 과정이 이것을 어떻게 다루는지 보겠습니다. 다음은 루트로부터 첫번째 과정을 거친 결과입니다 :

이후에 그들의 참조들도 마크합니다 :

그리고 그들의 참조도 반복합니다. 가능할때까지 :

이제 방문할 수 없는 객체들은 접근 불가능한것으로 간주되어 삭제될 것입니다.

이것이 가비지 콜렉터가 동작하는 개념입니다. 자바스크립트 엔진은 어플리케이션의 실행에 영향을 주지 않고 빠르게 수행되도록 하기 위해 많은 최적화 옵션을 적용하고 있습니다.

  • 세대별 수집 – 객체는 “새로운 객체”와 이전 객체” 두개의 세트로 나뉘어집니다. 많은 객체들은 나타나고 그들의 일을 수행하고 빨리 죽습니다. 이것들은 공격적으로 청소될 수 있을 것입니다. 하지만 충분히 오래 살아남은 객체들은 “오래된” 객체가 되어 덜 자주 검사를 받게 됩니다.
  • 증분 수집 – 많은 객체가 있고 이러한 많은 객체를 한번에 전부다 방문하며 마크하는 과정을 거치게 되면 실행에 눈에 띄는 지연이 발생할 수 있습니다. 그래서 엔진은 이러한 수거 작업을 여러 조각으로 나누어 수행합니다. 이렇게 나눈 조각의 변화를 추적할 수 있도록 부가적인 처리가 필요하지만 큰 한번의 딜레이 대신에 짧은 단위의 딜레이를 가질 수 있습니다.
  • 유휴 시간 수집 – 가비지 콜렉터는 CPU가 유휴상태일 때만 실행되어 어플리케이션의 실행에 끼치는 영향을 줄입니다.

이것 말고도 가비지 콜렉터의 다른 최적화 기능들이 있습니다. 각 엔진들이 구현하고 있는 추가적인 테크닉들이 있으며 엔진의 발전에 따라 지속적으로 변화하고 있습니다.

정리

알아야 하는 핵심은 다음과 같습니다.

  • 가비지 콜렉션은 자동으로 실행됩니다. 우리는 이것을 강제로 실행하거나 막을 수 없습니다.
  • 객체는 그들이 접근 가능한 동안 메모리에 유지됩니다.
  • 참조가 된다는 것이 루트(Root)에서 참조 가능한것과 같은 말은 아닙니다 : 상호 참조하고 있는 객체들이 전체에서 보면 참조 불가능할 수 있습니다.

참고 : https://javascript.info/garbage-collection