일반적으로 ssh에 접속하기 위해 다음과 같은 명령어를 사용한다.

$ ssh edward@dev.haruair.com

사실 단순해 보이지만 개발자는 게을러야 하므로 ~/.ssh/config에 설정을 작성해두면 더 짧게 사용할 수 있다. ~/.ssh/config가 없다면 빈 파일을 만들면 된다. 파일 내용은 다음과 같다.

Host dev
    HostName dev.haruair.com
    User edward

이렇게 작성하면 다음과 같이 접속 가능하다. (만약 동작하지 않는다면 퍼미션을 확인해주세요. ChangWan Jun님이 chmod 440 ~/.ssh/config 식으로 퍼미션 지정이 필요하다고 알려주셨습니다.)

$ ssh dev

해당 서버가 ssh key를 기본값인 id_rsa를 사용하고 있다면 접속에 문제가 없다. (ssh key를 생성하는 방법은 이 페이지를 참조) 하지만 각각 서버마다 다른 키를 사용하고 있다면 여전히 -i 플래그를 이용해야 해서 번거롭다.

$ ssh dev -i ~/.ssh/haruair.dev
$ ssh company -i ~/.ssh/edward.company

각각 서버마다 어떤 키를 참조할지 config에 미리 작성해둘 수 있다.

Host dev
    HostName dev.haruair.com
    User edward
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/haruair.dev

Host company
    HostName dev.haruair.company
    User edward
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/edward.company

내 경우엔 GitHub용 키를 별도로 생성해서 등록했는데 다음과 같이 쓸 수 있다.

Host github.com
    User git
    IdentityFile ~/.ssh/haruair.github

이렇게 등록해두면 다음과 같이 해당 주소의 ssh를 사용할 때 해당 키를 참조하게 된다.

$ git clone git@github.com:haruair/some-repo.git

2차 도메인 등의 경우, 다음과 같이 와일드카드로도 지정이 가능하다.

Host *.haruair.com
    User edward
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/haruair.dev

config 파일은 상당히 세세한 범위까지 설정이 가능한데 그 내용은 ssh_config 메뉴얼에서 확인할 수 있다.

ssh key를 생성하고 서버에 등록하는 방법은 복잡하지 않다. ssh-keygen으로 공개키/비밀키 한 쌍을 생성하고, 공개키 내용을 접속할 서버에 ~/.ssh/authorized_keys에 저장하면 해당 서버에 비밀번호 없이 ssh 접속이 가능하다.

다음은 클라이언트에서 인증키를 생성하는 방법이다.

$ ssh-keygen -t rsa -C "edward@haruair.com"
Enter file in which to save the key (/home/user/.ssh/id_rsa): /home/user/.ssh/my_ssh_key # 키이름을 넣음
Enter passphrase (empty for no passphrase): ********** # 최초 등록시 사용할 비밀문구를 입력함

-t는 키의 타입이 rsa인지 dsa인지 정하는 플래그고 -C는 코멘트를 남기는 플래그다.

위와 같이 입력하면 my_ssh_key와 my_ssh_key.pub이 생성되는데 *.pub 파일이 공개키로 서버에 등록하면 비밀번호 없이 접속이 가능해진다. 공개키의 내용을 확인하는 방법은 다음과 같다.

$ cat /home/user/.ssh/my_ssh_key.pub

위 명령어를 입력하면 터미널 상에 공개키 내용이 출력된다. 해당 내용을 복사해두자. 이제 이 내용을 서버에 접속해서 ~/.ssh/authorized_keys에 공개키를 추가해준다. 다음은 해당 서버에서 입력할 명령어다.

$ cat >> ~/.ssh/authorized_keys
(이 상태에서 복사한 공개키를 붙여 넣고 엔터를 눌러 줄을 바꾼 후 Ctrl+D를 누르면 저장된다.)

이제 비밀번호 없이 인증키로 로그인이 가능하다.

파일 하나의 변경 이력을 한번에 확인해야 할 때가 가끔 있다. 물론 GUI 도구들이 워낙 잘 되어 있어서 쉽게 파악이 가능한 부분이지만 콘솔에서 필요할 때 다음의 명령어를 활용할 수 있다.

git log는 다양한 기능을 가지고 있는데 단순히 커밋 로그만 보여주는 것 외에도 포맷을 달리 하거나 diff를 같이 보여준다거나 하는 기능이 있다. 여기서는 단일 파일을 확인하는 방법을 위주로 살펴보려 한다.

다음 명령어는 해당 파일이 커밋된 기록을 한번에 확인할 수 있다.

$ git log <filename>

이 목록을 diff의 결과처럼 라인별 변경 사항을 확인하고 싶다면 -p 플래그를 사용할 수 있다.

$ git log -p <filename>

내용이 너무 많으면 -<숫자> 플래그로 출력 수를 정할 수 있다.

$ git log -p -5 <filename>

특정 키워드의 변경을 확인하고 싶다면 grep을 활용할 수 있다. grep\|를 넣어 OR 색인이 가능하다. 다음은 2011070102 라는 텍스트와 함께 커밋 해시, 커밋한 사용자, 일자를 출력하는 예다.

$ git log -p <filename> | grep '2011070102\|commit \|Author:\|Date:'

1.7.2 이후에서는 --word-diff라는 플래그가 추가되었는데 플래그 이름처럼 행마다의 diff가 아니라 단어 기준의 diff를 출력해준다. (문서를 리포지터리에서 관리한다면 정말 유용하다.) 물론 git diff에서도 사용 가능한 플래그다.

$ git log -p --word-diff <filename>

상세한 설명은 Git의 기초 – 커밋 히스토리 조회하기 페이지에서 확인할 수 있다.

2013년 2월 한국 다녀올 당시 레오폴드 키보드를 구입해서 왔다. 당시 회사에서 사용했던 이상한 키배열의 삼성 노트북 이후 키보드에 관심이 생겨서 관련 글을 읽다보니 기계식 키보드에 대해 좋은 인상을 받아 구입했다.

구입할 때 직접 타건해보고 구입하지 못하는 상황이었다. 그래서 타건 영상을 보고 타이핑 소리를 가늠해야 했는데 다들 청축은 지나치게 시끄럽고 적축은 오타율이 높다는 얘기를 듣고 갈축을 선택했다. 다른 키보드 경험이 없어 딱히 비교하기 어려운데 간단하게 적어보자면 다음과 같다.

  • 소리가 좋다. 그래서 가끔 사무실이 조용하면 사용하기 민망하다.
  • 기계식 키보드라 그런지 키보드 자체가 무겁다. 덕분에 키보드가 밀리거나 하지 않는다.
  • 키배열이 윈도 배열이라 맥 단축키가 불편할 때가 있다. (바인딩으로 해결 안되는 부분)
  • 타이핑하는 양이 많아질 때 일반 키보드에 비해 손에 무리가 덜 간다.
  • 키는 여전히 하얗지만 본체는 약간 노란 빛이 감돌기 시작했다.

처음엔 집에서 사용했는데 이제 사무실에 가져다놓고 가상머신에서 윈도우 작업할 때 주로 사용하고 있다. 사무실에서 사용할 때 맥 키보드랑 혼용해서 사용하고 있다. 가장 큰 이유는 키보드 바인딩 문제인데 F1~12의 펑션키를 맥 키보드의 기능처럼 쓰게되면 해당 function키가 정상적으로 동작하지 않는다거나 생각처럼 깔끔하게 바인딩이 되질 않아 어쩔 수 없이 혼용하고 있다.

전반적으로 키보드는 만족스럽지만 키보드 사용량이 그렇게 많지 않다면 크게 유의미하진 않은 것 같다. 만약 내가 vim이나 emacs를 잘 사용할 줄 안다면 더 재미있게 사용할 수 있지 않았을까. 코드든 글이든 타이핑을 많이 하는 사람이라면 사용해보는 것도 나쁘지 않다.

아직 키를 분리해서 청소해보진 않았는데 조만간 날 잡아서 분리하고 청소를 해야겠다.

주말에 아티클을 보다가 관심이 생겨 OpenCV를 잠깐 살펴봤다. OpenCV는 Computer Vision 오픈소스 라이브러리로, 제공하는 예제를 통해 Face Tracking 등을 구현해볼 수 있다. 초보자를 위한 튜토리얼은 많은데 생각처럼 잘 안되는 부분들이 있어 이 글을 작성했다. 이 글에서는 Mac OSX, xcode를 사용했다.

OpenCV는 brew를 통해서도 설치할 수 있는데, 내가 놓치고 있는 부분이 있는지 잘 안되서 리포지터리에서 받아 컴파일했다.

먼저 컴파일을 위해 cmake를 brew로 설치한다.

$ brew install cmake

리포지터리를 복제한 후 build, install까지 진행한다.

$ git clone https://github.com/Itseez/opencv.git && cd opencv
$ mkdir build
$ cd build
$ cmake -G "Unix Makefiles" ..
$ make -j8
$ sudo make install

이렇게 설치는 완료된다. /usr/local/lib에 설치된 libopencv-*.dylib를 확인할 수 있다.

예제 구동 과정

예제 코드를 그대로 실행하면 바로 되어야 하는데, 리포지터리에 올라가 있는 예제 프로젝트는 xcode 버전 문제로 열리지 않았다. 다음과 같은 과정으로 예제 코드를 확인할 수 있다.

  1. XCode를 실행해 Command Line Tool 프로젝트 생성
  2. Project Navigator에서 우클릭, “Add File To…” 클릭
  3. 파일 선택창이 뜨면 “/”를 입력해 네비게이션 패널을 불러와 /usr/local/lib을 입력
  4. libopencv_<어쩌고>.dylib 을 모두 선택. (아래 예제에서는 core, highgui, imgproc, objdetect만 있어도 구동 가능.)
  5. 프로젝트 파일을 눌러 Build Settings에서 “Header Search Paths”에 /usr/local/include, “Library Search Paths”에 /usr/local/lib을 추가.
  6. 예전 버전의 cascade를 내려받음
  7. 프로젝트 파일을 눌러 Build Phases > Copy Files에 위에서 받은 xml 파일을 추가하고 “Destination”을 Products Directory로 설정

위 과정을 끝내고 나면 main.cpp를 열어 코드를 다음과 같이 작성한다. 이 코드는 opencv 리포지터리에 있는 MacOSX 예제 코드다.

#include <CoreFoundation/CoreFoundation.h>
#include <cassert>

// Example showing how to read and write images
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

const char  * WINDOW_NAME  = "Face Tracker";
const CFIndex CASCADE_NAME_LEN = 2048;
char    CASCADE_NAME[CASCADE_NAME_LEN] = "~/haarcascade_frontalface_alt2.xml";

using namespace std;

int main(int argc, char** argv)
{

    const int scale = 2;

    // locate haar cascade from inside application bundle
    // (this is the mac way to package application resources)
    CFBundleRef mainBundle  = CFBundleGetMainBundle ();
    assert (mainBundle);
    CFURLRef    cascade_url = CFBundleCopyResourceURL (mainBundle, CFSTR("haarcascade_frontalface_alt2"), CFSTR("xml"), NULL);
    assert (cascade_url);
    Boolean     got_it      = CFURLGetFileSystemRepresentation (cascade_url, true,
                                                                reinterpret_cast<UInt8 *>(CASCADE_NAME), CASCADE_NAME_LEN);

    if (! got_it)
        abort ();

    // create all necessary instances
    cvNamedWindow (WINDOW_NAME, CV_WINDOW_AUTOSIZE);
    CvCapture * camera = cvCreateCameraCapture (CV_CAP_ANY);
    CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*) cvLoad (CASCADE_NAME, NULL, NULL, NULL);
    CvMemStorage* storage = cvCreateMemStorage(0);
    assert (storage);

    // you do own an iSight, don't you ?!?
    if (! camera)
        abort ();

   // did we load the cascade?!?
    if (! cascade)
        abort ();

    // get an initial frame and duplicate it for later work
    IplImage *  current_frame = cvQueryFrame (camera);
    IplImage *  draw_image    = cvCreateImage(cvSize (current_frame->width, current_frame->height), IPL_DEPTH_8U, 3);
    IplImage *  gray_image    = cvCreateImage(cvSize (current_frame->width, current_frame->height), IPL_DEPTH_8U, 1);
    IplImage *  small_image   = cvCreateImage(cvSize (current_frame->width / scale, current_frame->height / scale), IPL_DEPTH_8U, 1);
    assert (current_frame && gray_image && draw_image);

    // as long as there are images ...
    while ((current_frame = cvQueryFrame (camera)))
    {
        // convert to gray and downsize
        cvCvtColor (current_frame, gray_image, CV_BGR2GRAY);
        cvResize (gray_image, small_image, CV_INTER_LINEAR);

        // detect faces
        CvSeq* faces = cvHaarDetectObjects (small_image, cascade, storage,
                                            1.1, 2, CV_HAAR_DO_CANNY_PRUNING,
                                            cvSize (30, 30));

        // draw faces
        cvFlip (current_frame, draw_image, 1);
        for (int i = 0; i < (faces ? faces->total : 0); i++)
        {
            CvRect* r = (CvRect*) cvGetSeqElem (faces, i);
            CvPoint center;
            int radius;
            center.x = cvRound((small_image->width - r->width*0.5 - r->x) *scale);
            center.y = cvRound((r->y + r->height*0.5)*scale);
            radius = cvRound((r->width + r->height)*0.25*scale);
            cvCircle (draw_image, center, radius, CV_RGB(0,255,0), 3, 8, 0 );
        }

        // just show the image
        cvShowImage (WINDOW_NAME, draw_image);

        // wait a tenth of a second for keypress and window drawing
        int key = cvWaitKey (100);
        if (key == 'q' || key == 'Q')
            break;
    }

    // be nice and return no error
    return 0;
}

코드를 빌드 및 실행하면 앱이 실행되어 얼굴의 위치를 트래킹하는 모습을 확인할 수 있다. (신기해!)

트러블 슈팅

  • CASCADE_NAME~로 시작하지만 컴파일된 파일을 기준으로 하는 상대 경로.
  • cvLoad()에 에러가 생길 땐 xml 경로, 파일명 문제, 디버깅용 라이브러리를 넣었을 때 등의 문제라고. 내 경우에는 최신 버전의 xml이라서 에러가 났다. 위 과정처럼 구버전의 xml을 쓰면 구동은 되지만 별로 내키지 않는 해결 방법. (코드는 개선되었는데 예제는 예전 방식으로 되어 있다거나 한 것 같다.)

{{< youtube //www.youtube.com/embed/ZRXE7lX8wA8 >}}

위 영상은 Dropbox의 창업자인 Drew Houston의 MIT 졸업축사로 페이스북에서 보게 되었는데 인상적인 부분을 적어둔다.

  • ‘사람은 자신이 가장 많은 시간을 함께 보내는 다섯명의 평균치다’. 잠시 생각해보자. 자신의 서클(자주 어울리는 집단)에 포함된 다섯명은 누구인가?
  • 내가 깨달은 점은 자기 주변을 자신에게 영감을 줄 수 있는 사람으로 채우는 것이 재능, 근면보다 중요하다는 사실.
  • 무슨 일을 하든 세상에서 가장 뛰어난 사람들이 모이는 곳은 단 한 곳 뿐이며 여러분은 그곳으로 가야한다. 다른 곳에서 안주하지 마라.
  • 인생을 완벽하게 만들려고 노력하기 보다는 인생을 모험으로 만들 수 있는 자유의 날개를 통해 보다 높이 날자.

이전에도 핀터레스트 창업자 Ben Silbermann의 Y콤비네이터 스타트업 스쿨 스피치를 보고 많은 자극을 받았는데 오랜만에 다시 마음을 가지런히 할 수 있는 좋은 축사였다. 자막은 이종욱님의 작품인데 페이스북 계정을 통해 자극이 되는 좋은 이야기와 영상을 공유해주시는 편이라 follow 하길 권한다.

트위터를 통해 읽게 된 Javascript, the New PHP 라는 아티클은 JavaScript가 PHP를 그대로 답습하고 있음을 몇 사례를 들어 담담하게 이야기한다. 쉽게 개발할 수 있다는 장점과 단점으로 만들어진 PHP 생태계와 마찬가지로 JavaScript도 비슷한 수순을 밟고 있어 안타깝다는 이야기가 전체적인 흐름이다. 특히나 나도 비슷한 기술 스택을 가지고 있어 공감하는 부분이 많았다.

  • 쉽고 간단한 코드가 깊은 레벨로의 학습보다 복사-붙여넣기로 지금 당장 동작하는 코드를 선호하게 한다.
  • 이런 문제를 야기하는 개발자들 스스로가 공부하는 것을 귀찮아 하거나 필요하다는걸 깨닫지 못하고 있다.
  • 결과적으로, 이미 다 알고 있다고 생각하고 스스로를 전문가로 생각한다.

위 아티클에서 인용된 Stackoverflow의 안좋은 답변에서는 초심자를 위해 어떻게 질문에 접근해야 하는가에 대해 이야기하지만 그보다 더 눈 여겨 보게 된 부분은 합당한 답변보다 단순히 기술적으로 옳은 답변(지금 당장에 동작하는 코드)를 선택함으로 잘못된 코드가 계속 생명력을 갖게 된다는 점이다. 결국 낮은 수준의 개발자들이 낮은 수준의 개발자를 끊임 없이 양산한다. 커뮤니티는 보수적으로 변하고 새로운 기술에 기민하게 대응하지 못하게 된다.

깊이있게 알지 못하는 개발자를 정죄하자는 이야기는 아니다. 다만 내 스스로 쉬운 언어 속의 옅은 개발자로 삶을 살고 있는건 아닌지 경계해봐야 한다.


tl;dr

VMWare Tools를 다시 설치하면 된다.

VMWare Tools 재설치하기

문제

맥에서 VMFusion를 사용해 Windows 개발 환경을 쓰고 있었는데 8.1로 업데이트 한 이후 공유 폴더 드라이브에 연결하지 못하는 문제가 나타났다.

VMFusion에서는 맥과 윈도우 환경을 가상 드라이브를 통해 연결하는데 이 설정에서 윈도우 기본 폴더들을 맥 환경의 디렉토리로 연결해둘 수 있다. Desktop도 그런 방식으로 공유되어 있는 상태였는데 읽을 수는 있으나 저장이 안되는 이상한 상황이라 이것 저것 둘러보다가 가상 드라이브가 없어진 상태라는 것을 알게 되었다. Sharing에 문제가 있다고 로그아웃 하라는 얘기는 계속 나오지만 로그아웃 해도 해결되지 않았다.

VMWare Tools를 재설치하니 정상적으로 드라이브를 인식해 다시 사용할 수 있었다.

해결 과정

  1. 일단 VM을 종료한다.
  2. 메뉴에서 Virtual Machine > Sharing > Sharing Settings...에 들어가 공유를 끈다.
  3. VM을 실행하고 로그인해서 바탕화면까지 진입한다.
  4. 메뉴에서 Virtual Machine > Update VMWare Tools 또는 Virtual Machine > Reinstall VMWare Tools을 선택한다. VMWare Tools를 내려받는 과정 후 가상 이미지가 자동으로 추가되어 설치가 진행된다. 자동으로 진행되지 않으면 내 컴퓨터 > D: 에 있는 설치 프로그램을 실행해준다.
  5. 설치가 완료되면 재시동 하라고 하는데 재시동 한다.
  6. 다시 메뉴에서 Virtual Machine > Sharing > Sharing Settings...에 들어가 공유를 켠다.
  7. 재시동 된 VM에서 로그인한다. VM Sharing 설정이 변경되어 다시 로그인하라는 메시지가 뜨면 다시 로그인한다.

다른 프로그래밍 언어와 같은 부분이 많아 큰 어려움은 없었지만 타입 변환 등 자바스크립트만의 특성으로 잘 읽어봐야 할 부분이 많았다. 특히 typeofinstanceof 부분은 JavaScript를 더 이해하는데 도움이 되었다.


Chapter 4 표현식과 연산자 Expressions and Operators

표현식 : 값을 구하기 위한 표현 방법

연산자 : 표현식의 구성요소 (값을 반환하거나 연산함)

4.1 Primary Expressions

상수, 문자열 값, 키워드, 변수 레퍼런스 (eg. true, false, undefined)

4.2 객체, 배열 생성자 Object and Array Initializers

  • 객체 : {}
  • 배열 : []

4.3 함수 정의 표현식

var square = function(x) { return x * x; };

4.4 프로퍼티 접근 표현식

expression.identifier  // 식별자가 이렇게 써도 문법에 위배되지 않을 때 사용 가능
expression[expression] // 배열일 때 특히

4.5 구동 표현식 Invocation Expressions

square(10)
Math.min(x, y, z)
foos.sort()
  • 아규먼트의 연산을 먼저 한 후 함수가 실행됨 (in common sense)
  • 실행 가능 객체가 아니라면 TypeError
  • return
  • 함수(OO에선 메서드)가 실행될 때 this

4.6 객체 생성 표현식

var o = new Object()
var d = new Date()
  • 초기화 할 때 생성되는 this

4.7 연산자 미리보기

  • p.62 표 참조. 다른 프로그래밍 언어랑 크게 다른게 없음.
  • typeof, instanceof
  • 문자열 합칠 때와 숫자 계산할 때 둘 다 + 사용
  • 형변환이 자유롭게 발생하므로 유의 "3" * "5" // => 15

4.8 산술 표현식 Arithmetic Expressions

  • *, /, %, +, –
  • NaN
    • 연산자 : 값을 합칠 때 해당 값이 객체이면 3.8.3에서 본 것처럼 toString() 또는 valueOf()로 형변환 시도
  • 비트연산자 : &, |, ^, ~, <<, >>, >>>

4.9 관계 표현식 Relational Expressions

  • ===== : 타입 변환 허용 var point = { x : 10, y: 20 };

    “x” in point // => true

    “s” in point // => false

    var data = [3, 2, 1];

    “0” in data // => true

    // 배열은 값이 들어 있는지 확인하는 것이 아니라 해당 index에 값이 있는지 확인해줌

  • instanceof : 어떤 클래스인지 확인할 때. 상속되는 모든 클래스에 대해 true

4.10 논리 표현식

  • &&, ||, !

4.11 배정 표현식 Assignment Expressions

  • =, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, |=, ^=

4.12 평가 표현식 Evaluation Expressions

  • eval(): 하나의 아규먼트를 js 코드처럼 처리해줌.
  • ES5에서 global eval
  • IE 전용 execScript()

4.13 그 외 표현식

  • 3항 연산자(?:) : var age = birthyear > 2000 ? "yong" : "old";
  • typeof : p.82 표 참조
    • “undefined”, “object”, “boolean”, “number”, “string”, “function”, “object”, “<구현한 객체>”
    • null"object" 반환
    • 호출 가능한 객체 callable object 가 정확하게 함수function는 아님. 하지만 typeof에서는 호출 가능한 객체에 대해 “function”을 반환
  • delete
    • ES5 strict에선 못지울 것 지우면 SyntaxError
    • garbage collection이 있으므로 일일이 지워줄 필요는 없음
  • void : 프로토콜에서 사용하는데 (주. 쓰지말자.)
  • , : a=1, b=2, c=3;

tmux를 어디선가 보고 엄청 멋지다 싶어서 검색했더니 @nanhapark님이 재미있게 정리한 글이 있어서 편하게 볼 수 있었다. 읽은 글들은 다음 목록에서 확인할 수 있고, 읽으며 요약해 정리했다. (nodeqa.com에 해당 글이 있었는데 사이트가 더이상 운영되지 않아 링크를 제거했다.)

tmux 설치하기

맥에서는 brew로 설치 가능

$ brew install tmux

tmux 구성

  • session : tmux 실행 단위. 여러개의 window로 구성.
  • window : 터미널 화면. 세션 내에서 탭처럼 사용할 수 있음.
  • pane : 하나의 window 내에서 화면 분할.
  • status bar : 화면 아래 표시되는 상태 막대.

명령어 정리

tmux는 prefix 키인 ctrl+b를 누른 후 다음 명령 키를 눌러야 동작할 수 있다. 다음 내용에서 ctrl + b, 어쩌고 내용이 있다면 tmux 내에서 쓸 수 있는 단축키다.

ctrl + b, <key>

일부 직접 명령어를 입력해야 할 때는 명령어 모드로 진입해야 한다. 명령어 모드의 key는 :다.

ctrl + b, :

세션 관련

# 새 세션 생성
$ tmux new -s <session-name>

# 세션 이름 수정
ctrl + b, $

# 세션 종료
$ (tmux에서) exit

# 세션 중단하기 (detached)
ctrl + b, d

# 세션 목록 보기 (list-session)
$ tmux ls

# 세션 다시 시작
$ tmux attach -t <session-number or session-name>

윈도우 관련

# 새 윈도우 생성
ctrl + b, c

# 세션 생성시 윈도우랑 같이 생성
$ tmux new -s <session-name> -n <window-name>

# 윈도우 이름 수정
ctrl + b, ,

# 윈도우 종료
ctrl + b, &
ctrl + d

# 윈도우 이동
ctrl + b, 0-9 : window number
            n : next window
            p : prev window
            l : last window
            w : window selector
            f : find by name

틀 pane 관련

# 틀 나누기
ctrl + b, % : 횡 분할
          " : 종 분할

# 틀 이동
ctrl + b, q 그리고 화면에 나오는 숫자키
ctrl + b, o : 순서대로 이동
ctrl + b, arrow : 방향키로 숑숑

# 틀 삭제
ctrl + b, x
ctrl + d

# 틀 사이즈 조절
(ctrl + b, :)
resize-pane -L 10
            -R 10
            -D 10
            -U 10

# 틀 레이아웃 변경
ctrl + b, spacebar

단축키 관련

# 단축키 목록
ctrl + b, ?

# 키 연결 및 해제 bind and unbind
(ctrl + b, :)
bind-key [-cnr] [-t key-table] key command [arguments]
unbind-key [-acn] [t key-table] key

# 옵션 설정 `set` and `setw`
set -g <option-name> <option-value>  : set-option
setw -g <option-name> <option-value> : set-window-option

copy mode 1

copy mode에서는 콘솔을 스크롤 하거나 내용을 복사하는 등의 기능을 할 수 있다.

# copy mode 진입
ctrl + b, [

# 빠져나오기
(copy mode에서) q or ESC

# 이동
arrow : 커서 이동
pageUp, pageDown : 페이지 이동 (iTerm에서는 fn + up, down, terminal에서는 alt + up, down)

설정 저장하기 tmux.conf

~/.tmux.conf 파일을 생성해 설정을 저장해두면 시작할 때 자동으로 설정을 불러온다. 컬러 설정, 마우스 설정


이것저것 해보다 알게 된건데 iTerm에서 tmux로 만든 세션을 다른 terminal에서 접속하면 동시에 동작한다.

뭐야 무서워…

  • 내 터미널 설정이 이상해서 그런지 설명대로 동작하질 않는다. 
  • 색상을 바꿔요

    눈에 편한 색상을 골라보세요 :)

    Darkreader 플러그인으로 선택한 색상이 제대로 표시되지 않을 수 있습니다.