본문으로 건너뛰기
  1. Posts/

Linux 입문자를 위한 핵심 가이드

·37 분
NineKoo9
작성자
NineKoo9
목차

이 글은 생활코딩의 Linux 강의를 수강하며, 별도로 궁금했던 부분을 조사하고 내용을 추가하여 정리한 학습 노트입니다.

리눅스 시스템의 구조를 이해하고, 터미널에서 자유롭게 명령어를 다루기 위한 기초 학습 자료입니다.

1. 리눅스 파일 시스템의 구조 (Directory Structure)
#

리눅스는 윈도우와 달리 드라이브(C:, D:) 개념이 없고, 루트(/) 라는 최상위 디렉토리에서 모든 것이 시작되는 트리(Tree) 구조를 가집니다.

디렉토리설명 및 용도
/ (Root)파일 시스템의 최상위 시작점입니다.
/binUser Binaries. 필수적인 기본 명령어들이 위치합니다. 최신 시스템에서는 /usr/bin의 링크인 경우가 많습니다.
/sbinSystem Binaries. 시스템 관리용 필수 명령어들이 위치합니다. 최신 시스템에서는 /usr/sbin의 링크인 경우가 많습니다.
/libLibraries. 시스템과 프로그램들이 실행되는 데 필요한 공유 라이브러리 파일들이 위치합니다.
/home사용자 홈 디렉토리. 윈도우의 C:\Users\와 비슷합니다. cd ~ 명령어로 바로 이동할 수 있습니다.
/etc설정 파일(Configuration). 프로그램이나 시스템의 전반적인 설정 파일들이 모여 있습니다.
/var가변 데이터(Variable). 로그 파일(log), 데이터베이스 등 내용이 계속 바뀌거나 늘어나는 파일들이 저장됩니다.
/tmp임시 파일(Temporary). 많은 배포판에서 부팅 시 또는 주기적으로 정리되지만 항상 보장되지는 않으므로 민감한 데이터를 두지 마세요.
/usrUser System Resources. 배포판이 제공하는 대부분의 사용자용 프로그램과 라이브러리가 설치됩니다. 수동 설치는 보통 /usr/local(또는 /opt)에 둡니다.

2. 필수 기본 명령어 (Navigation & File Operation)
#

터미널에서 현재 위치를 파악하고 파일/폴더를 조작하는 명령어들입니다.

위치 확인 및 이동
#

  • pwd (Print Working Directory): 현재 내가 어떤 경로에 위치해 있는지 보여줍니다.
    pwd
    # 출력 예시: /home/ubuntu
  • cd (Change Directory): 디렉토리를 이동합니다.
    • cd ~: 내 홈 디렉토리로 즉시 이동.
    • cd ..: 상위(부모) 디렉토리로 이동.
    • Tip: 경로를 입력할 때 Tab 키를 누르면 자동 완성이 됩니다. (오타 방지 및 속도 향상)

파일 및 디렉토리 조작
#

  • ls (List): 현재 디렉토리의 파일 목록을 보여줍니다.
    • ls -a: 숨김 파일(점으로 시작하는 파일)까지 모두 표시.
    • ls -l: 파일의 권한, 소유자, 크기 등 자세한 정보를 리스트 형태로 표시.
    • ls -al: 위 두 옵션을 합쳐서, 숨김 파일을 포함한 모든 파일의 상세 정보를 표시.
  • mkdir (Make Directory): 새로운 디렉토리를 만듭니다.
    • mkdir -p dir1/dir2: 부모 디렉토리까지 한 번에 생성합니다. (-p는 parents)
  • cp (Copy): 파일이나 디렉토리를 복사합니다.
    cp [원본] [대상]
    cp -r [원본_디렉토리] [대상_디렉토리] # 디렉토리 복사 시 -r 옵션 필수
  • mv (Move): 파일을 이동하거나 이름을 변경할 때 사용합니다.
    mv old_name.txt new_name.txt  # 이름 변경
    mv file.txt /home/user/docs/  # 파일 이동
  • rm (Remove): 파일을 삭제합니다.
    • rm -r [디렉토리]: 디렉토리를 삭제합니다. (내부 파일 포함)
    • rm -f [파일]: 확인 메시지 없이 강제로 삭제합니다. (-f는 force)
    • 매우 위험: rm -rf / 같은 명령어는 시스템 전체를 삭제할 수 있으니 절대 함부로 사용해서는 안 됩니다.

파일 내용 확인 및 생성
#

  • touch: 비어있는 새 파일을 생성하거나, 기존 파일의 최종 수정 시간을 현재 시간으로 변경합니다.
    touch new_file.txt
  • cat: 파일의 전체 내용을 터미널에 한 번에 출력합니다. 짧은 파일에 유용합니다.
  • less: cat과 달리, 긴 파일의 내용을 페이지 단위로 편하게 볼 수 있게 해줍니다. (이동: 방향키, 종료: q)
    less large_log_file.log
  • head / tail: 파일의 앞부분 또는 뒷부분 10줄을 기본으로 출력합니다.
    • head -n 5 log.txt: 앞부분 5줄만 보기
    • tail -f log.txt: 파일에 내용이 추가될 때마다 실시간으로 계속 보여주기 (-f는 follow)
  • file: 파일의 종류(텍스트, 이미지, 실행 파일 등)를 알려줍니다.
    • file my_script.sh -> Bourne-Again shell script, ASCII text executable

3. 시스템 권한과 패키지 관리
#

권한 (Permissions)
#

  • sudo (SuperUser Do): 관리자(Root) 권한으로 명령을 실행합니다. 시스템 설정 변경이나 프로그램 설치 시 필수입니다. 왜 권한이 필요한지에 대해서는 14장에서 자세히 다룹니다.

패키지 매니저 (Package Manager)
#

앱스토어처럼 프로그램을 손쉽게 설치, 업데이트, 삭제해주는 도구입니다. OS별로 명령어가 다릅니다.

계열대표 OS패키지 매니저주요 명령어
DebianUbuntu, Debian, Mintaptsudo apt update
sudo apt install [패키지]
(apt-get도 널리 사용됨)
Red HatRHEL, Fedora, CentOSdnfsudo dnf install [패키지]
(구형 시스템에서는 yum 사용)
ArchArch Linux, Manjaropacmansudo pacman -Syu
sudo pacman -S [패키지]
macOS-Homebrew (brew)brew install [패키지]
brew upgrade

Java 개발자 Tip: JDK 설치 Java(JDK)는 패키지 매니저로 간편하게 설치할 수 있습니다.

# Ubuntu에서 OpenJDK 17 설치
sudo apt install openjdk-17-jdk

# 여러 Java 버전이 설치된 경우, 사용할 버전 선택
sudo update-alternatives --config java

또 다른 방법: SDKMAN! 여러 JDK 버전을 자주 바꿔야 한다면 sdkman이 편리합니다.

# 설치
curl -s "https://get.sdkman.io" | bash
# 예) Temurin 21 설치 및 기본 설정
sdk install java 21-tem
sdk default java 21-tem

기업/보안망에서는 설치 스크립트 내용을 먼저 확인하고 사내 정책을 준수하세요.

파일 다운로드와 API 테스트
#

wget
#

wget은 URL을 통해 파일을 다운로드하는 간단하고 직관적인 도구입니다.

  • wget [URL]: URL의 파일을 현재 디렉토리에 원본 파일명 그대로 다운로드합니다.
  • wget -O [저장할파일명] [URL]: 다운로드한 파일의 이름을 지정해서 저장합니다.

curl (웹 개발자의 필수품)
#

curl(cURL)은 HTTP, HTTPS, FTP 등 다양한 프로토콜을 이용해 데이터를 전송하는 강력한 도구입니다. 단순 파일 다운로드뿐만 아니라, 웹 개발 시 API와 상호작용하는 거의 모든 상황을 흉내 낼 수 있어 ‘터미널 계의 Postman/Insomnia’로 불립니다.

  • 기본 GET 요청 (API End-point 확인) API가 잘 작동하는지 가장 간단하게 확인할 때 사용합니다.

    # 특정 사용자 정보를 요청하는 API를 터미널에서 바로 테스트
    curl https://jsonplaceholder.typicode.com/users/1 
  • 헤더 정보 확인 (-i, -I) HTTP 상태 코드(200 OK, 404 Not Found 등)나 응답 헤더(Content-Type, Cache-Control 등)를 확인할 때 매우 유용합니다.

    # 응답 본문과 헤더를 함께 보기
    curl -i https://example.com
    
    # 헤더 정보만 보기 (HEAD 요청)
    curl -I https://example.com
  • POST 요청 (API에 데이터 전송) 로그인, 회원가입, 데이터 생성 등 클라이언트가 서버로 데이터를 보낼 때의 상황을 테스트합니다.

    # JSON 데이터를 API 서버로 전송하는 예제
    curl -X POST \
         -H "Content-Type: application/json" \
         -d '{"title": "foo", "body": "bar", "userId": 1}' \
         https://jsonplaceholder.typicode.com/posts
    • -X POST: HTTP 요청 메소드를 ‘POST’로 지정합니다. (-X GET, -X PUT, -X DELETE 등도 가능)
    • -H "Content-Type: ...": 요청 헤더를 설정합니다. REST API는 대부분 JSON을 사용하므로 필수적인 옵션입니다.
    • -d '{"json": "data"}': 요청에 담아 보낼 데이터(Request Body)를 지정합니다.
  • 리다이렉트 따라가기 (-L) 요청한 URL이 다른 주소로 리다이렉트 될 때, 그 최종 목적지까지 따라가서 내용을 가져옵니다.

    # http://t.co/... 와 같은 단축 URL의 실제 내용을 볼 때 사용
    curl -L http://t.co/I5YYd9dDA

4. 입출력 제어와 파이프라인(I/O Redirection)
#

명령어의 결과(출력)를 화면이 아닌 파일로 저장하거나, 파일의 내용을 명령어의 입력으로 넣을 때 사용합니다.

핵심 개념: 표준 스트림
#

  1. Standard Input (stdin, 0): 입력 (주로 키보드)
  2. Standard Output (stdout, 1): 출력 (주로 모니터)
  3. Standard Error (stderr, 2): 에러 메시지

명령어는 실행될 때 기본적으로 이 세 가지 입출력 통로를 가지고 시작합니다. 운영체제는 이 통로들에 0, 1, 2라는 번호를 붙여 관리하는데, 이를 파일 디스크립터(file descriptor, fd) 라고 부릅니다.

여기서 “file"이라는 단어 때문에 실제 파일만 가리키는 것처럼 보이지만, 꼭 그렇지는 않습니다. 터미널, 파일, 파이프, 소켓 같은 입출력 대상 전체를 가리키는 번호표에 가깝습니다.

그래서 쉘의 리다이렉션 문법은 결국 이렇게 읽으면 됩니다.

  • 0 = 입력 통로(stdin)
  • 1 = 일반 출력 통로(stdout)
  • 2 = 에러 출력 통로(stderr)

즉, 2> 같은 문법은 “2번 통로(stderr)를 다른 곳으로 보내라” 는 뜻입니다.

왜 숫자와 >가 같이 붙을까?
#

쉘은 “몇 번 통로를 어디로 보낼지” 를 숫자와 기호로 표현합니다.

  • > 는 사실 1> 의 축약형입니다. 즉, ls > result.txtls 1> result.txt 와 같은 뜻입니다.
  • 2>2번 통로(stderr) 를 파일로 보냅니다.
  • < 는 파일 내용을 0번 통로(stdin) 로 연결합니다.

예를 들면:

ls > result.txt      # stdout(1) -> result.txt
rm nofile.txt 2> error.log   # stderr(2) -> error.log
sort < list.txt      # list.txt -> stdin(0)

&는 무엇일까? 왜 2>&1처럼 쓰일까?
#

초보자가 가장 많이 헷갈리는 부분이 바로 여기입니다.

먼저 명령어 끝에 붙는 &2>&1 안의 & 는 같은 모양이지만 문맥이 다릅니다.

  • sleep 100 &&백그라운드 실행을 뜻합니다.
  • 2>&1&“파일 이름이 아니라, 다른 파일 디스크립터를 가리킨다” 는 뜻입니다.

즉, 2>&1 은 이렇게 읽으면 됩니다.

  • 2>: 2번 통로(stderr)를 대상으로 한다
  • &1: 파일 이름 1이 아니라, 1번 통로(stdout)가 현재 향하는 곳을 가리킨다

그래서 2>&1 의 뜻은:

“stderr(2)도 stdout(1)이 현재 가는 곳으로 보내라”

입니다.

이 차이를 꼭 구분해야 합니다.

rm nofile.txt 2>1

위 명령은 에러를 1이라는 이름의 파일에 저장합니다.
즉, 1을 파일 이름으로 본 것입니다.

반면:

rm nofile.txt 2>&1

이 명령은 에러(stderr)를 stdout과 같은 곳으로 보냅니다.
여기서 &1은 파일이 아니라 1번 출력 통로를 뜻합니다.

리다이렉션 기호
#

  • >: 실행 결과(stdout)를 파일에 덮어쓰기로 저장합니다. (1>의 축약형)
    ls -l > result.txt
  • >>: 실행 결과를 파일의 끝에 추가합니다.
    ls -al >> result.txt
  • <: 파일의 내용을 명령어의 입력(stdin)으로 사용합니다.
    sort < list.txt # list.txt의 내용을 정렬하여 화면에 출력
  • 2>: 에러(stderr)만 따로 파일에 저장합니다.
    rm nofile.txt 2> error.log
  • &> 또는 > ... 2>&1: 출력(stdout)과 에러(stderr)를 모두 같은 곳으로 보냅니다. 자동화 스크립트에서 로그를 남길 때 자주 씁니다.
    # 명령어의 모든 결과(출력+에러)를 all.log에 덮어쓰기
    # &> all.log은 > all.log 2>&1의 단축 표현(Bash/Zsh 전용)입니다.
    ls -l /nonexistent &> all.log 
    
    # POSIX 호환까지 고려하면 이 형태가 더 보편적입니다.
    ls -l /nonexistent > all.log 2>&1
    
    # 명령어의 모든 결과를 all.log에 추가하기
    ls -l /nonexistent >> all.log 2>&1 

여기서 중요한 포인트가 하나 더 있습니다. 2>&1“stderr를 stdout과 같은 곳으로 보내라” 는 뜻이지, 파일을 직접 지정하는 문법이 아닙니다. 그래서 보통은 > 파일명 또는 >> 파일명함께 사용합니다.

예를 들어:

command > all.log 2>&1

이 문장은 이렇게 읽습니다.

  1. stdout(1)을 먼저 all.log로 보낸다.
  2. stderr(2)도 stdout이 현재 가는 곳(all.log) 으로 보낸다.

즉, 최종적으로 stdout과 stderr가 둘 다 all.log로 들어갑니다.

반대로 순서를 바꾸면 결과가 달라질 수 있습니다.

command 2>&1 > all.log

이 경우에는 먼저 stderr가 현재 stdout이 향하는 곳(보통 터미널) 을 따라가고, 그 다음에야 stdout만 all.log 로 바뀝니다.
즉, 초보자 입장에서는 > all.log 2>&1 순서를 외우는 편이 안전합니다.

파이프라인 (Pipeline, |)
#

하나의 프로그램의 **출력(stdout)**을 다른 프로그램의 **입력(stdin)**으로 연결하는 컨베이어 벨트입니다.

  • 활용 예시:
    • ps aux | grep apache: 현재 실행 중인 모든 프로세스(ps) 중에서 “apache"만 필터링해서 봅니다.
    • ls -l | wc -l: 파일 리스트의 줄 수를 세어 현재 디렉토리의 파일/폴더 개수를 확인합니다.

grep (필터링)
#

파이프라인과 자주 함께 쓰이며, 원하는 문자열이 포함된 행(Line)만 검색하는 강력한 도구입니다.

  • 자주 쓰는 옵션:
    • -i (Ignore case): 대소문자를 구분하지 않고 찾습니다.
    • -r (Recursive): 하위 디렉토리까지 모두 검색합니다.
    • -n (Line number): 결과가 몇 번째 줄에 있는지 함께 출력합니다.
    • -w (Word): 정확히 그 단어만 검색합니다. (예: grep -w "is"는 “this"를 제외)
    • -v (Invert): 해당 단어가 포함되지 않은 줄만 출력합니다.

5. 쉘(Shell)과 커널(Kernel)
#

사용자가 입력한 명령어가 하드웨어까지 전달되는 과정입니다.

  1. User: 명령어를 입력 (예: ls)
  2. Shell: 사용자의 명령어를 해석하여 커널로 전달 (번역가 역할)
  3. Kernel: 하드웨어를 제어하여 실제 작업을 수행
  4. Hardware: 물리적인 연산 수행

쉘의 종류
#

  • bash: 대부분의 리눅스 시스템에 기본으로 설치된 가장 대중적인 쉘.
  • zsh: bash와 호환되면서, 강력한 플러그인(oh-my-zsh)과 테마, 자동완성 기능으로 생산성을 크게 높여주는 쉘.
  • fish: ‘Friendly Interactive SHell’의 약자로, 별도 설정 없이도 유용한 기능(자동 제안, 문법 강조 등)을 바로 쓸 수 있어 초보자에게 친화적인 쉘.
  • echo $0 또는 ps -p $$: 현재 사용 중인 쉘을 확인하는 명령어.

쉘 스크립트 (Shell Script)
#

반복적인 작업을 자동화하기 위해 명령어들을 파일에 모아두고 한 번에 실행하는 ‘대본’입니다.

  • Shebang (#!): 스크립트 파일의 가장 첫 줄에 작성하며, 이 파일을 어떤 쉘로 해석할지 지정합니다.
    #!/bin/bash
    # 위 코드는 아래 내용을 /bin/bash 쉘을 통해 실행하라는 의미
    echo "Hello Shell Script!"

쉘 스크립트 실행
#

  1. 실행 권한 부여:
    chmod +x my_script.sh
  2. 스크립트 실행:
    ./my_script.sh

6. 리눅스 파일 검색 명령어 정리
#

1. locate (데이터베이스 기반 빠른 검색)
#

미리 만들어진 파일 목록 데이터베이스(DB)를 검색하여 매우 빠르지만, 방금 생성한 파일은 못 찾을 수 있습니다.

  • 사용법: locate [파일이름]
  • DB 업데이트(필요시): sudo updatedb

2. find (실시간 정밀 검색)
#

느리지만 현재 파일 시스템을 직접 탐색하여, 이름, 종류, 크기, 시간 등 매우 다양한 조건으로 검색할 수 있습니다.

  • 기본 구조: find [경로] [옵션] [표현식]
    • 경로: / (전체), . (현재 디렉토리), ~ (홈 디렉토리)
  • 주요 옵션:
    • 이름: -name "*.log" (대소문자 구분), -iname "*.log" (대소문자 무시)
    • 타입: -type f (파일), -type d (디렉토리)
    • 크기: -size +100M (100MB 초과), -size -10k (10KB 미만)
    • 시간: -mtime -7 (7일 이내 수정), -mtime +7 (7일 이전에 수정)
  • 검색 후 작업 수행 (-exec, -delete):
    • -exec는 검색된 파일에 대해 다른 명령을 실행하는 강력한 기능입니다.
    • {}는 검색된 파일을 의미하며, \;는 명령어의 끝을 알립니다.
    # .tmp 파일을 찾아 삭제 전 확인 메시지 띄우기 (안전)
    find . -name "*.tmp" -exec rm -i {} \;
    
    # .tmp 파일을 그냥 바로 삭제 (효율적)
    find . -name "*.tmp" -delete

7. 파일의 위치와 실행 원리
#

7.1. 명령어의 실제 위치 찾기 (which, whereis)
#

  • which [명령어]: $PATH 환경 변수에 설정된 경로만 검색하여, 실행될 명령어의 절대 경로를 찾아줍니다.
    $ which ls
    /bin/ls
  • whereis [명령어]: 실행 파일뿐 아니라, 소스 코드, 매뉴얼 파일의 위치까지 알려줍니다.

7.2. 명령어 실행의 비밀 ($PATH)
#

lsjava처럼 짧게만 입력해도 명령이 실행되는 이유는 쉘이 $PATH 라는 환경 변수에 등록된 디렉토리들을 순서대로 검색해 실행 파일을 찾아주기 때문입니다.

  • 확인 방법: echo $PATH

Java 개발자 Tip: JAVA_HOME과 PATH Java 설치 후 JAVA_HOME 환경 변수를 설정하고, 이 변수를 이용해 $PATH에 JDK의 bin 디렉토리를 추가하는 것이 표준적인 방법입니다. 이렇게 해야 java, javac, jps 등 JDK의 모든 명령어를 어디서든 사용할 수 있습니다.

# ~/.bashrc 또는 ~/.zshrc 에 추가
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH

8. 컴퓨터 구조와 프로세스의 이해
#

8.1. 프로그램 vs 프로세스
#

  • 프로그램 (Program): 스토리지(HDD, SSD)에 저장된, 실행되기 전의 정적인 코드 덩어리입니다. (예: /bin/ls 실행 파일)
  • 프로세스 (Process): 프로그램이 실행되어 메모리에 올라가고, CPU에 의해 처리되고 있는 동적인 상태입니다. 모든 프로세스는 고유한 ID(PID)를 가집니다.

8.2. 부모와 자식 프로세스 (Parent-Child Process)
#

리눅스의 모든 프로세스는 ‘가족 관계’처럼 계층 구조를 이룹니다. 이 개념을 이해하면 export가 왜 중요한지 완벽하게 이해할 수 있습니다.

비유: “붕어빵 틀과 붕어빵”

  1. 부모 프로세스 (Parent Process): 현재 실행 중인 프로세스입니다. (예: 여러분이 명령어를 입력하는 터미널(쉘))
  2. 자식 프로세스 (Child Process): 부모 프로세스가 생성한 새로운 프로세스입니다.

어떻게 만들어지나요? (Fork & Exec)

여러분이 터미널(부모)에서 java -jar my-app.jar 라는 명령어를 실행하면, 내부적으로 두 가지 일이 순식간에 일어납니다.

  1. Fork (복제): 터미널(부모)이 자기 자신과 거의 똑같은 복사본, 즉 ‘자식’을 만듭니다. 마치 붕어빵 틀로 똑같은 모양을 찍어내는 것과 같습니다. 이 시점의 자식은 부모와 거의 모든 것을 공유합니다. (특히 환경 변수!)
  2. Exec (실행): 복제된 ‘자식’이 자신의 모습을 버리고, java라는 새로운 프로그램으로 갈아입습니다. 이제 이 자식은 JVM 프로세스가 되어 my-app.jar를 실행하기 시작합니다. 붕어빵 틀에서 나온 반죽에 팥소를 넣어 진짜 붕어빵으로 완성하는 과정과 같습니다.

Java 개발자에게 왜 중요한가요? 바로 이 Fork(복제) 단계에서 부모의 ‘환경 변수’가 자식(JVM)에게 상속되기 때문입니다. 터미널(부모)에서 export SPRING_PROFILES_ACTIVE=prod 와 같이 설정하면, 자식인 java 프로세스는 이 값을 그대로 물려받습니다. Spring Boot 애플리케이션은 System.getenv()를 통해 이 환경 변수를 읽어들여, prod 프로파일용 application-prod.yml 설정을 로드하게 됩니다. export를 쓰지 않은 ‘쉘 변수’는 부모의 방에서만 쓰이는 비밀번호라 자식에게 상속되지 않는 것입니다.

프로세스 관계 확인하기
#

ps 명령어에 옵션을 주면 부모-자식 관계를 직접 확인할 수 있습니다. PPID가 바로 부모 프로세스의 ID입니다.

# 현재 내가 실행한 프로세스들의 ID(PID)와 부모 ID(PPID)를 함께 보기
ps -o pid,ppid,comm

#   PID  PPID COMMAND
# 12345  1234 bash      (내 터미널, 부모)
# 54321 12345 ps        (ps 명령어를 실행한 자식)

pstree 명령어를 사용하면 이 관계를 나무 구조로 훨씬 더 명확하게 볼 수 있습니다. (설치 필요: sudo apt install psmisc)

pstree

이 부모-자식 관계와 상속의 개념은 리눅스 시스템 동작 방식의 핵심 중 하나입니다.


9. 프로세스 모니터링과 관리
#

9.1. 프로세스 목록 확인 (ps)
#

  • ps -ef: 시스템의 모든 프로세스를 표준 포맷으로 상세하게 조회합니다. ps aux와 유사하지만, System V 계열(많은 리눅스 배포판)에서 더 표준으로 사용됩니다.
  • ps -ef | grep java: 시스템에서 실행 중인 모든 java 프로세스를 필터링하여 확인합니다.

9.2. 실시간 모니터링 (top & htop)
#

  • top: 전통적인 실시간 프로세스 모니터링 도구.
  • htop: top보다 시각적으로 보기 편하고 사용하기 쉬운 업그레이드 버전. (설치 필요: sudo apt install htop)
    • Load Average: 시스템 부하 평균. CPU 코어 수보다 높으면 과부하 상태로 볼 수 있습니다.

Java 개발자 Tip: JDK 내장 모니터링 도구 ps 외에 JDK가 제공하는 도구를 사용하면 JVM에 특화된 정보를 얻을 수 있습니다.

  • jps: 현재 실행 중인 Java 프로세스의 PID와 Main 클래스 이름을 보여줍니다. ps -ef | grep java 보다 훨씬 깔끔합니다.
  • jstat -gc [PID] 1000: 해당 PID의 Java 프로세스에 대해 GC(Garbage Collection) 상태를 1초마다 보여줍니다. 메모리 누수나 GC 튜닝 시 유용합니다.

10. 멀티태스킹과 백그라운드 실행
#

10.1. 포그라운드 vs 백그라운드
#

  • 포그라운드 (Foreground): 명령어를 실행하면 터미널의 제어권을 갖고 사용자 입력을 기다리는 상태.
  • 백그라운드 (Background): 터미널 제어권 없이 뒤에서 조용히 실행되는 상태.

10.2. 작업 제어
#

  1. 프로세스 중단 (Ctrl + C): 포그라운드에서 실행 중인 프로세스에 **종료 신호(SIGINT)**를 보내 중단시킵니다.
  2. 프로세스 일시 정지 (Ctrl + Z): 실행 중인 프로세스를 멈추고 백그라운드로 보냅니다. (종료가 아님)
  3. 백그라운드 작업 확인 (jobs): 백그라운드에 있는 작업들의 목록과 번호(Job ID)를 확인합니다.
  4. 백그라운드에서 실행 (&): 명령어 뒤에 &를 붙이면 처음부터 백그라운드에서 실행됩니다.
    # 터미널을 종료해도 프로세스가 죽지 않도록 nohup과 함께 사용
    nohup java -jar my-app.jar &
    • Tip: &만 사용하면 터미널 세션이 끊길 때 프로세스도 함께 종료됩니다. nohup (No Hang Up) 명령어와 함께 사용해야 터미널 연결이 끊겨도 프로세스를 계속 실행시킬 수 있습니다. 출력과 에러는 nohup.out 파일에 자동으로 저장됩니다.
    • 더 안정적인 운영을 위해서는 nohup보다 11장에서 배울 systemd를 사용하는 것이 좋습니다.
  5. 작업 전환 (fg, bg):
    • fg %[작업번호]: 백그라운드 작업을 다시 포그라운드로 가져옵니다. (Foreground)
    • bg %[작업번호]: 일시 정지된 작업을 백그라운드에서 계속 실행시킵니다. (Background)

10.3. 프로세스 종료: kill의 기술
#

kill은 단순히 프로세스를 ‘죽이는’ 것이 아니라, 특정 **‘신호(Signal)’**를 보내는 섬세한 명령어입니다.

1단계: PID 찾기 (feat. jps, pgrep)
#

프로세스를 제어하려면 먼저 고유 번호인 **PID(Process ID)**를 알아야 합니다.

# 1. jps로 깔끔하게 찾기 (추천)
jps

# 2. pgrep으로 이름으로 찾기
pgrep -f "my-app.jar"
# 출력 예시: 12345

2단계: 신호 보내기 (상황에 맞게)
#

  • kill -15 [PID] 또는 kill [PID] (가장 일반적이고 안전한 종료)

    • SIGTERM (15) 신호를 보냅니다. 이는 “애플리케이션아, 이제 정리하고 곱게 종료해줘” 라는 정중한 요청입니다.
    • Java 개발자 Tip: 잘 만든 Spring Boot 애플리케이션은 이 신호를 받으면, 진행 중인 HTTP 요청을 안전하게 마무리하고, DB 커넥션 풀을 닫고, 메시지 큐 리스너를 중지하는 등 우아한 종료(Graceful Shutdown) 과정을 수행합니다. 항상 이 방법을 먼저 시도해야 합니다.
  • kill -9 [PID] (최후의 수단, 강제 종료)

    • SIGKILL (9) 신호를 보냅니다. 이는 프로세스가 무시할 수 없는 강제 종료 명령입니다. “지금 당장 모든 걸 멈추고 사라져!” 라는 의미입니다.
    • Java 개발자 Tip: kill -15로도 종료되지 않는 응답 불가(Hang) 프로세스를 정리할 때만 사용하세요. 이미 종료된 뒤 부모가 수거하지 못한 좀비 프로세스kill -9로도 사라지지 않으니, 부모 프로세스를 종료하거나 systemd가 회수하도록 처리해야 합니다. JVM이 정리 작업(Shutdown Hook)을 할 시간조차 주지 않고 즉시 사라지므로, 데이터가 손상되거나 불완전한 상태로 남을 수 있습니다.
  • kill -1 [PID] (설정 리로드)

    • SIGHUP (1) 신호를 보냅니다. 많은 서버 프로그램들이 이 신호를 받으면 설정 파일을 다시 읽어들입니다(reload).
    • 웹 개발자 Tip: Nginx 같은 웹 서버의 설정을 변경한 뒤, 서버를 중단시키지 않고 변경사항을 적용하고 싶을 때 사용합니다. (Java 애플리케이션 자체는 보통 이 신호를 직접 처리하지 않으므로, Java 앱 재시작이 필요 없는 Nginx 재설정에 주로 사용됩니다.)

3단계: 이름으로 한번에 제어 (pkill)
#

PID를 일일이 찾기 번거로울 때, 프로세스 이름을 이용해 신호를 보냅니다.

# "my-app.jar"를 실행하는 모든 java 프로세스에 정중한 종료 신호 보내기
pkill -f "my-app.jar"

# "hung-app.jar"처럼 응답하지 않는 프로세스에 강제 종료 신호 보내기
pkill -9 -f "hung-app.jar"

11. 데몬(Daemon)과 서비스 관리 (systemd)
#

11.1. 데몬의 개념과 systemd
#

  • 데몬(Daemon): 시스템 백그라운드에서 항상 실행되며 특정 요청을 처리하기 위해 대기하는 프로그램. (예: 웹 서버, DB 서버)
  • systemd: 현대 리눅스 배포판의 표준 시스템 및 서비스 관리자입니다. 과거의 init.d 스크립트나 service 명령어를 대체하여 데몬(서비스)을 관리합니다.

11.2. 서비스 제어 (systemctl)
#

systemctlsystemd를 제어하는 핵심 명령어입니다. (대부분 sudo 필요)

  • 서비스 시작/중지:
    sudo systemctl start apache2.service
    sudo systemctl stop apache2.service
  • 서비스 재시작/상태 확인:
    sudo systemctl restart apache2.service
    sudo systemctl status apache2.service
    (.service 확장자는 생략 가능)

11.3. 부팅 시 자동 실행 설정
#

  • 자동 실행 활성화:
    sudo systemctl enable apache2
  • 자동 실행 비활성화:
    sudo systemctl disable apache2
  • 활성화 여부 확인:
    sudo systemctl is-enabled apache2
    이 명령어들은 과거 /etc/rc.d/ 디렉토리에 심볼릭 링크를 만들던 복잡한 과정을 대체합니다.

11.4. Java 애플리케이션을 위한 systemd 서비스 등록 (실무 핵심)
#

nohup은 간단하지만, 서버가 재부팅되면 앱이 자동으로 실행되지 않고 로그 관리도 불편합니다. 현대 리눅스에서는 systemd를 사용해 Java 애플리케이션을 안정적인 ‘서비스’로 등록하고 관리합니다.

  1. 서비스 파일 생성: /etc/systemd/system/ 경로에 [서비스명].service 파일을 생성합니다. (예: my-app.service)

    sudo vim /etc/systemd/system/my-app.service
  2. 서비스 파일 작성:

    [Unit]
    Description=My Spring Boot Application
    After=network.target
    
    [Service]
    User=myuser
    Group=mygroup
    ExecStart=/usr/bin/java -jar /home/myuser/app/my-app-0.0.1.jar --spring.profiles.active=prod
    SuccessExitStatus=143
    Restart=on-failure
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    • Description: 서비스에 대한 설명.
    • User/Group: 서비스를 실행할 사용자 및 그룹. 보안을 위해 root가 아닌 전용 계정을 만드는 것이 좋습니다.
    • ExecStart: 애플리케이션을 실행하는 명령어. 반드시 절대 경로를 사용해야 합니다.
    • SuccessExitStatus=143: kill -15 (SIGTERM)로 정상 종료되었을 때의 종료 코드(143)를 성공으로 간주합니다.
    • Restart=on-failure: 서비스가 비정상적으로 종료되면 10초 후에 자동으로 재시작합니다.
    • WantedBy=multi-user.target: 시스템이 부팅될 때 이 서비스를 시작하도록 설정합니다.
    • 환경 변수 관리 팁: 서비스 파일에 Environment="SPRING_PROFILES_ACTIVE=prod"를 추가하거나 EnvironmentFile=/etc/default/my-app로 외부 파일을 읽게 하면, 코드와 비밀 값을 분리해 운영할 수 있습니다. (systemctl edit my-app으로 드롭인 파일을 만드는 것이 업데이트 시 충돌을 줄이는 안전한 방법입니다.)
  3. 서비스 적용 및 시작:

    • sudo systemctl daemon-reload: 새로 만든 서비스 파일을 systemd가 인식하도록 리로드합니다.
    • sudo systemctl start my-app: 서비스를 시작합니다.
    • sudo systemctl status my-app: 서비스 상태를 확인합니다. (초록색 active (running) 확인)
    • sudo journalctl -u my-app -f: my-app 서비스의 로그를 실시간으로 확인합니다. (tail -f와 유사)
  4. 부팅 시 자동 실행 설정:

    • sudo systemctl enable my-app: 시스템이 부팅될 때 자동으로 서비스가 시작되도록 활성화합니다.
    • sudo systemctl disable my-app: 자동 시작을 비활성화합니다.

12. 정기적 작업 스케줄링 (Cron)
#

12.1. Cron의 개념 및 설정
#

정기적으로 반복되는 작업(백업, 로그 정리 등)을 자동화하는 도구입니다.

  • 설정 파일 수정: crontab -e
  • 설정 목록 확인: crontab -l
  • Tip: crontab -e 실행 시 기본 편집기가 불편하다면 export EDITOR=nano 명령으로 편집기를 변경한 후 사용해 보세요.

12.2. 작성 문법
#

형식: 분 시 일 월 요일 명령어 (왼쪽부터 순서대로)

필드의미범위
몇 분에0-59
몇 시에0-23
매달 며칠에1-31
몇 월에1-12
요일무슨 요일에0-7 (0과 7이 일요일)
  • *: ‘매번’을 의미합니다.
  • 예시:
    # 매일 새벽 2시 10분에 백업 스크립트 실행
    10 2 * * * /home/user/backup.sh > /var/log/backup.log 2>&1

13. 쉘 설정 자동화: 나만의 개발 환경 구축하기
#

개발을 하다 보면 git status를 수시로 입력하거나, cd ~/projects/my-awesome-project처럼 긴 경로로 계속 이동하는 등 매번 반복하는 작업이 많습니다. 또한 DATABASE_URL이나 API_KEY 같은 정보를 터미널에 임시로 입력하기도 합니다.

매번 터미널을 열 때마다 이런 환경이 자동으로 설정된다면 훨씬 편하겠죠? 그 역할을 하는 것이 바로 ‘쉘 시작 스크립트’를 이용한 설정 자동화입니다.

13.1. 쉘 설정의 핵심: 시작 파일, 변수, 그리고 별칭
#

1) 쉘 시작 파일: .bashrc.zshrc (설정 레시피)
#

새 터미널을 실행하면, 쉘(요리사)은 ~/.bashrc~/.zshrc 같은 ‘레시피 노트’를 읽어 적힌 명령어들을 실행하며 작업 환경을 구성합니다.

  • .bashrc: BASH 쉘이 시작될 때마다 읽는 개인 맞춤 설정집입니다.
  • .zshrc: ZSH 쉘이 시작될 때마다 읽는 개인 맞춤 설정집입니다.

둘의 용도는 완벽히 동일하며, 내가 어떤 쉘을 쓰느냐에 따라 사용하는 ‘설정집’ 이름만 달라집니다. 아래 명령어로 현재 사용 중인 쉘을 확인할 수 있습니다.

# 내가 지금 쓰는 쉘 확인하기 ("bash" 또는 "zsh" 등이 출력됨)
echo $0

2) 변수의 두 종류: 쉘 변수 vs 환경 변수 (내 방 vs 우리 집)
#

export는 쉘 설정에서 가장 중요하고 헷갈리는 개념입니다. **“우리 집”**과 “내 방” 비유로 확실히 이해해 봅시다.

  • 터미널(쉘) = ‘나’
  • 쉘 변수 (API_KEY="12345") = ‘내 방’에서만 쓰는 비밀 암호. 오직 ‘나’(현재 터미널)만 알고 있으며, ‘내’가 실행하는 다른 프로그램(자식 프로세스)은 알 수 없습니다.
  • 환경 변수 (export API_KEY="12345") = ‘우리 집’ 모든 가족이 아는 공용 와이파이 비밀번호. ‘나’와 ‘내’가 실행하는 모든 자식들(프로그램)이 함께 사용하도록 상속됩니다.
  • export 명령어 = ‘내 방 비밀 암호’를 ‘가족 공용 암호’로 승격시켜 모두에게 알려주는 확성기 역할.

여기서 초보자가 가장 많이 하는 질문이 바로 이것입니다.

~/.bashrc에 그냥 MY_VAR=hello라고만 적어도, 어차피 쉘이 자식을 만들 때 fork 하니까 자식도 그 값을 복사받는 것 아닌가요?”

헷갈릴 만한 질문이고, 핵심은 fork만 있는 것이 아니라 실제 외부 명령 실행은 보통 fork + exec로 이어진다는 점입니다.

조금 더 정확히 풀면:

  1. 현재 쉘이 자식 프로세스를 만들 때 먼저 fork()를 한다.
  2. 그 다음 자식은 exec()java, python, env 같은 다른 프로그램으로 갈아탄다.
  3. 이때 새 프로그램에게 전달되는 것은 쉘 내부 상태 전체가 아니라, export된 환경 변수 목록이다.

즉, MY_VAR=hello처럼 export 없이 만든 쉘 변수는 현재 쉘 안에서는 보이지만, exec()되어 실행된 외부 프로그램에게는 기본적으로 전달되지 않습니다.

그래서 아래 두 줄은 전혀 다르게 동작합니다.

MY_VAR=hello
export OTHER_VAR=world
  • MY_VAR현재 쉘 내부에서만 아는 값
  • OTHER_VAR현재 쉘 + 이후 실행할 자식 프로세스도 아는 값

즉, 차이는 단순히 “변수냐 환경 변수냐"가 맞습니다. 다만 실무에서는 이 차이가 자식 프로세스에게 전달되느냐 아니냐를 결정하므로 매우 중요합니다.

Java 개발자에게 export가 필수인 이유 터미널에서 java -jar my-app.jar로 Spring Boot 애플리케이션을 실행하면, 이 java 프로세스는 터미널의 ‘자식’ 프로세스가 됩니다. Spring Boot 코드의 application.yml에서 ${MY_API_KEY}와 같이 환경 변수를 사용하거나, 코드 내에서 System.getenv("MY_API_KEY")를 통해 값을 읽으려고 할 때, 부모인 터미널이 exportMY_API_KEY를 설정해주지 않았다면 자식인 Java 프로세스는 이 값을 절대 알 수 없습니다. 그래서 “API 키가 정의되지 않았습니다” 같은 에러가 발생하거나 설정값이 비어있게 됩니다. 외부 자식 프로세스에 값을 전달하려면 반드시 export를 사용해야 합니다.

3) 변수의 영속성: 임시 변수 vs 영구 변수 (포스트잇 vs 레시피)
#

터미널에서 설정한 변수는 왜 창을 닫으면 사라질까요? 임시 저장소(메모리)와 영구 저장소(파일)의 차이 때문입니다.

  • 터미널에서 직접 export (임시, 포스트잇) export MY_VAR="hello"를 터미널에 입력하는 것은, 현재 터미널의 메모리(RAM)에 내용을 임시로 기록하는 것과 같습니다. 마치 모니터에 포스트잇을 붙이는 것과 같아서, 터미널을 닫으면 함께 사라집니다.

  • 설정 파일에 export 작성 (영구, 레시피) 변수를 영구적으로 만들려면, export MY_VAR="hello"라는 명령어 자체를 ~/.bashrc 같은 설정 파일에 기록해야 합니다. 이 파일은 “쉘이 시작될 때마다 이 명령들을 실행해서 환경을 재구성해라"라고 알려주는 **‘설정 레시피’**입니다. 새 터미널이 열릴 때마다 쉘은 이 레시피를 읽어 다시 ‘포스트잇’을 붙여주는 셈입니다.

여기서도 중요한 차이가 있습니다.

# ~/.bashrc
MY_VAR="hello"

이렇게만 적으면, 새 터미널이 열릴 때마다 그 쉘 자신은 MY_VAR를 알게 됩니다.
즉, 터미널 안에서 echo "$MY_VAR" 는 잘 동작할 수 있습니다.

하지만 그 터미널에서 실행한 외부 프로그램은 다릅니다.

python3 -c 'import os; print(os.getenv("MY_VAR"))'

이런 프로그램은 MY_VAR를 못 볼 수 있습니다. 왜냐하면 MY_VAR는 아직 쉘 변수일 뿐이고, 환경 변수로 export되지 않았기 때문입니다.

반대로:

# ~/.bashrc
export MY_VAR="hello"

이렇게 적어두면:

  • 새 터미널이 열릴 때 쉘이 MY_VAR를 만든다.
  • 동시에 이 값을 환경 변수로 등록한다.
  • 그 이후 이 쉘이 실행하는 java, python, node, env 같은 자식 프로세스도 그 값을 전달받을 수 있다.

즉, ~/.bashrc에 적는다는 사실만으로 자식 프로세스 전달이 보장되는 것은 아닙니다.
“설정 파일에 적었는가?”"export 했는가?” 는 서로 다른 문제입니다.

정리하면:

  • ~/.bashrcMY_VAR=hello 를 적는다 새로 열린 쉘 자신이 그 값을 사용 가능
  • ~/.bashrcexport MY_VAR=hello 를 적는다 새로 열린 쉘 + 그 쉘이 실행하는 자식 프로세스도 사용 가능

또 하나 자주 헷갈리는 점이 있습니다. 새 터미널이 MY_VAR를 알고 있는 이유는, 부모 쉘이 무조건 그 값을 넘겨줬기 때문이 아니라 새 쉘이 자기 시작 파일(~/.bashrc)을 다시 읽어 실행했기 때문일 수도 있습니다.
즉, “새 터미널에서도 보인다"와 “외부 자식 프로세스에게 전달된다"는 같은 이야기가 아닙니다.

4) 나만의 명령어: 별칭(Alias) 만들기
#

자주 사용하는 길고 복잡한 명령어를 간단한 단축키처럼 만들 수 있습니다.

# 별칭(alias) 설정 예시
alias gs="git status"
alias ll="ls -alF"
alias bootRun="./gradlew bootRun"

이런 alias 설정들을 ~/.bashrc~/.zshrc 파일에 추가해두면, 새 터미널부터 바로 사용할 수 있습니다.

5) 현재 설정된 변수 확인하기
#

“그래서 지금 내 터미널(메모리)에 어떤 ‘포스트잇’들이 붙어있지?“가 궁금할 때 사용하는 명령어입니다.

  • env 또는 printenv: 현재 쉘의 환경 변수(‘가족 공용 암호’)만 골라서 보여줍니다.
  • set: 환경 변수쉘 변수(‘가족 공용 암호’와 ‘내 방 비밀 암호’)를 모두 보여줍니다.

13.2. 심화: 로그인 쉘과 인터랙티브 쉘 (.bash_profile vs .bashrc)
#

쉘은 시작 방식에 따라 다른 설정 파일을 읽습니다. 이 미묘한 차이를 알면 좋습니다.

  • .bash_profile (로그인 쉘)

    • 언제?: 시스템에 로그인할 때 딱 한 번 실행됩니다. (예: ssh로 원격 서버에 처음 접속할 때)
    • 무엇을?: 한 번만 설정하면 되는 중요한 환경 변수 (JAVA_HOME 등)를 주로 설정합니다.
  • .bashrc (인터랙티브 쉘)

    • 언제?: 로그인이 아닌, 새로운 터미널 창을 열 때마다 매번 실행됩니다.
    • 무엇을?: 터미널을 편리하게 쓰기 위한 설정 (alias, 프롬프트 모양 등)을 주로 설정합니다.

Best Practice (이렇게만 쓰세요!)
#

위 구분이 헷갈린다면, 대부분의 리눅스 시스템에서 사용하는 모범 사례는 다음과 같습니다.

  1. 모든 설정을 .bashrc에 몰아넣습니다. (alias, export 등)

  2. .bash_profile에서는 .bashrc를 불러오기만 합니다. 아래 코드를 ~/.bash_profile에 추가하거나, 이미 있다면 주석을 해제하세요.

    # ~/.bash_profile
    
    if [ -f ~/.bashrc ]; then
       source ~/.bashrc
    fi

    이유: 이렇게 하면 ssh로 로그인할 때(.bash_profile 실행)도, 새 터미널을 열 때(.bashrc 실행)도 항상 동일하고 일관된 환경에서 작업할 수 있게 됩니다.

13.3. 설정 적용하기: source 명령어 (새로고침)
#

.bashrc 파일을 수정해도, 이미 열려 있는 터미널 창에는 변경사항이 적용되지 않습니다. 터미널은 시작될 때만 설정 파일을 읽기 때문입니다.

source는 **현재 쉘에 스크립트 파일의 내용을 바로 적용(새로고침)**하라는 명령어입니다.

# 1. ~/.bashrc 에 새로운 alias 추가 (예: alias gp="git push")
# 2. 터미널을 껐다 켤 필요 없이, 아래 명령어로 바로 적용!
source ~/.bashrc

# 이제 새로운 alias 'gp' 를 바로 사용할 수 있습니다.

source는 터미널을 재시작하지 않고도 설정을 즉시 반영할 수 있는, 매우 유용한 명령어입니다.


14. 다중 사용자와 권한 관리
#

리눅스는 여러 사용자가 동시에 접속해서 사용하는 ‘다중 사용자 시스템’입니다. 따라서 누가 어떤 파일에 접근하고 수정할 수 있는지를 통제하는 ‘권한’의 개념이 매우 중요합니다.

14.1. 사용자 확인 및 전환
#

  • id: **나(현재 사용자)**의 시스템 정보(UID, GID, 소속 그룹)를 확인합니다.
  • who: 현재 시스템에 로그인해 있는 모든 사용자 목록을 보여줍니다.
  • su - [ID]: 해당 사용자로 완전히 전환(로그인)합니다. (-는 해당 유저의 환경 설정까지 모두 불러오는 중요한 옵션)

14.2. 사용자 추가 및 관리
#

  • sudo useradd -m [ID]: 새로운 사용자를 추가합니다. (-m은 홈 디렉토리 생성 옵션)
  • sudo passwd [ID]: 사용자의 비밀번호를 설정하거나 변경합니다.
  • sudo usermod -aG [그룹명] [ID]: 사용자를 특정 그룹에 추가합니다. (예: sudo usermod -aG sudo myusermyuser에게 관리자 권한 부여)
  • sudo userdel -r [ID]: 사용자를 삭제합니다. (-r은 홈 디렉토리까지 모두 삭제)

14.3. 파일 권한(Permission) 완벽 이해
#

파일 권한은 특정 파일이나 디렉토리를 누가 읽고, 쓰고, 실행할 수 있는지 지정하는 규칙입니다. 권한은 세 종류의 사용자에 대해 정의됩니다.

  • Owner (User): 파일의 소유자.
  • Group: 소유자가 속한 그룹. (예: 개발팀, 관리자 그룹 등)
  • Other: 소유자도 아니고 그룹에도 속하지 않은 제3자(나머지 모든 사용자).

권한 정보 해석하기 (ls -l)
#

ls -l 명령은 파일과 디렉토리의 상세 정보를 보여주며, 이 중 권한 정보가 가장 중요합니다.

drwxr-x--- 8 keonhongkoo staff 256 1 15 2023 .android

  1. 파일 타입 (첫 글자)

    • d: 디렉토리 (Directory)
    • -: 일반 파일 (File)
    • l: 심볼릭 링크 (Symbolic Link)
  2. 권한 모드 (다음 9자리 문자열: rwxr-x---)

    • rwx (Owner): 첫 3자리는 소유자의 권한입니다. 읽기(r), 쓰기(w), 실행(x) 모두 가능합니다.
    • r-x (Group): 중간 3자리는 그룹의 권한입니다. 읽기(r)와 실행(x)만 가능합니다.
    • --- (Other): 마지막 3자리는 그 외 사용자의 권한입니다. 아무 권한도 없습니다.
  3. 기타 항목

    • 8: 하드 링크 수
    • keonhongkoo: 소유자 이름
    • staff: 그룹 이름
    • 256: 파일 크기 (Byte)
    • 1 15 2023: 마지막 수정 날짜
    • .android: 파일 또는 디렉토리 이름

권한의 의미: 파일 vs 디렉토리
#

r, w, x 권한은 대상이 파일이냐 디렉토리냐에 따라 의미가 달라집니다.

권한기호파일(File)에서의 의미디렉토리(Directory)에서의 의미
읽기r파일의 내용 보기 (cat, vim)디렉토리 내부의 파일 목록 조회 (ls)
쓰기w파일 내용 수정/저장디렉토리 내 파일 생성, 삭제, 이름 변경 (touch, rm, mv)
실행x파일을 프로그램으로 실행디렉토리 내부로 진입(접근) (cd)
  • 주의: 디렉토리에 x 권한이 없으면 cd로 들어갈 수 없습니다. 또한, 내 파일이라도 해당 파일이 담긴 디렉토리에 w 권한이 없으면 파일을 삭제하거나 이름을 바꿀 수 없습니다.

권한 변경: chmod (Change Mode)
#

chmod는 파일이나 디렉토리의 권한을 변경하는 명령어입니다. 두 가지 방식(Symbolic, Octal)이 있습니다.

1) 심볼릭(Symbolic) 방식: 직관적인 문자 사용

chmod [대상][연산자][권한] [파일명] 형태로 사용합니다.

  • 대상: u(user), g(group), o(other), a(all)
  • 연산자: +(추가), -(제거), =(지정)
  • 권한: r(read), w(write), x(execute)
# my_script.sh 파일에 대해...

# 소유자(u)에게 실행(x) 권한을 추가(+)
chmod u+x my_script.sh

# 그룹(g)에게 쓰기(w) 권한을 제거(-)
chmod g-w my_script.sh

# 모든 사용자(a)에게 읽기(r) 권한만 있도록 지정(=)
chmod a=r my_script.sh

2) 숫자(Octal) 방식: 빠르고 정확한 숫자 사용

권한을 숫자로 표현하여 조합합니다.

  • r (Read) = 4
  • w (Write) = 2
  • x (Execute) = 1

세 자리 숫자를 소유자-그룹-기타 순서로 지정합니다.

  • rwx = 4+2+1 = 7
  • rw- = 4+2 = 6
  • r-x = 4+1 = 5
  • r-- = 4
# 소유자는 rwx(7), 그룹은 r-x(5), 기타 사용자는 r-x(5) 권한을 부여
# 일반적인 실행 파일이나 웹 디렉토리 권한으로 많이 사용됨
chmod 755 my_script.sh 

# 소유자는 rw-(6), 그룹은 r--(4), 기타 사용자는 r--(4) 권한을 부여
# 일반적인 파일 권한으로 많이 사용됨
chmod 644 my_document.txt

# 모든 사용자에게 모든 권한을 부여 (보안상 주의 필요)
chmod 777 shared_folder
  • 재귀적 변경 (-R): 디렉토리와 그 안의 모든 하위 파일/폴더 권한을 한 번에 바꾸려면 -R 옵션을 사용합니다.
    # my_dir 내부의 모든 파일과 폴더에 755 권한 적용
    chmod -R 755 my_dir

소유권 변경: chown (Change Owner)
#

chown은 파일이나 디렉토리의 소유자 및 소유 그룹을 변경하는 명령어입니다. (주로 sudo 필요)

# my_file.txt의 소유자를 new_user로 변경
sudo chown new_user my_file.txt

# my_file.txt의 소유 그룹을 new_group으로 변경
sudo chown :new_group my_file.txt

# my_file.txt의 소유자와 그룹을 한 번에 변경
sudo chown new_user:new_group my_file.txt

# my_dir 디렉토리와 그 안의 모든 내용(-R)의 소유자를 new_user로 변경
sudo chown -R new_user my_dir

15. 네트워크 기초와 인터넷의 원리 (Network Basics)
#

서버를 다루기 위해서는 인터넷이 어떻게 연결되는지, 내 컴퓨터의 주소는 무엇인지 아는 것이 필수입니다.

15.1. 클라이언트와 서버 (Client & Server)
#

인터넷은 거대한 대화입니다. 이 대화는 정보를 요청하고 응답하는 두 주체로 이루어집니다.

  • 클라이언트 (Client, 손님): 웹 브라우저(Chrome 등)나 모바일 앱. 정보를 **요청(Request)**합니다.
  • 서버 (Server, 제공자): 리눅스 시스템 위에서 돌아가는 웹 서버 프로그램(Apache, Nginx, Node.js 등). 요청받은 정보를 찾아 **응답(Response)**합니다.
  • 동작 과정:
    1. 클라이언트: google.com 입력 (요청)
    2. 서버: index.html 파일 찾아서 전송 (응답)
    3. 클라이언트: 코드를 해석하여 화면에 그림

15.2. 주소 체계: IP와 도메인, 그리고 DNS
#

  • IP 주소 (IP Address): 컴퓨터가 통신을 위해 갖는 실제 숫자 주소 (예: 172.217.25.78). 전화번호에 비유됩니다.
  • 도메인 네임 (Domain Name): 사람이 기억하기 쉬운 문자 주소 (예: google.com). 전화번호부의 이름에 비유됩니다.
  • DNS (Domain Name System): 도메인 이름을 IP 주소로 바꿔주는 시스템입니다.
    • Tip: 터미널에서 ping google.com을 입력하면 해당 도메인의 실제 IP를 확인할 수 있습니다.

15.3. 공인 IP vs 사설 IP (중요)
#

서버 개발자가 가장 많이 헷갈리는 부분입니다. ip addr로 확인한 내 IP와 외부에서 보는 내 IP가 다를 수 있습니다.

  • 확인 방법:
    • 내부 IP: ip addr (결과 중 inet 뒤의 숫자, 예: 10.0.x.x, 192.168.x.x)
    • 외부 IP: curl ipinfo.io/ip (외부 인터넷 세상에서 나를 바라볼 때 보이는 IP)
종류설명비유특징
공인 IP (Public IP)통신사가 부여한 전 세계 유일한 주소회사의 대표 전화번호외부에서 직접 접속 가능
사설 IP (Private IP)공유기(Router)가 내부 기기들에 부여한 주소회사의 내선 번호외부에서 직접 접속 불가

백엔드 개발자 Tip: 클라우드(AWS) 환경에서는? AWS EC2 같은 클라우드 서버도 똑같습니다. 인스턴스를 생성하면 Public IP(외부 접속용)와 Private IP(VPC 내부 통신용)를 각각 할당받습니다. DB 서버는 보안을 위해 Private IP로만 통신하도록 설정하는 것이 일반적입니다.


16. 웹 서버 구축과 운영 (Web Server)
#

내 리눅스 컴퓨터를 전 세계에 정보를 제공하는 서버로 만드는 과정입니다. 전통적인 Apache 서버를 기준으로 설명하지만, 원리는 Nginx 등 다른 서버도 동일합니다.

16.1. 설치 및 실행 (Apache2)
#

우분투(Ubuntu) 기준 명령어입니다.

  1. 설치: sudo apt update && sudo apt install apache2
  2. 서비스 관리 (systemctl):
    • 실행: sudo systemctl start apache2
    • 상태 확인: sudo systemctl status apache2 (초록색 active (running)이 떠야 정상)
  3. 접속 테스트:
    • 브라우저 주소창에 http://localhost 또는 http://127.0.0.1 입력.
    • “Apache2 Default Page"가 보이면 성공입니다.

16.2. 설정 파일과 DocumentRoot
#

웹 서버가 “어디에 있는 파일을 보여줄지” 결정하는 설정입니다.

  • 설정 파일 위치: /etc/apache2/sites-enabled/000-default.conf
  • DocumentRoot: 사용자가 접속했을 때 보여줄 웹 페이지 파일(index.html)이 위치한 최상위 디렉터리입니다. (기본값: /var/www/html)
  • 실습: /var/www/html/index.html 파일을 sudo nano로 열어 내용을 수정해 보세요. 새로고침하면 내용이 바뀝니다.

16.3. 로그(Log) 분석: 서버의 블랙박스
#

서버에 문제가 생겼거나 해킹 시도가 의심될 때 가장 먼저 봐야 할 곳입니다.

  • 위치: /var/log/apache2/
  • access.log: 누가(IP), 언제, 무엇을 요청했는지 기록됩니다.
  • error.log: 서버 내부 오류나 실행 실패 원인이 기록됩니다.
  • 실시간 모니터링:
    tail -f /var/log/apache2/access.log

Java 개발자 Tip: Apache vs Nginx (Reverse Proxy) 최근 실무에서는 Apache보다 Nginx를 더 많이 사용하는 추세이며, 특히 Java 진영에서는 거의 표준처럼 사용됩니다.

  • Nginx의 역할: Nginx는 가볍고 대용량 트래픽 처리에 매우 효율적인 웹 서버입니다. 사용자의 요청(80, 443 포트)을 직접 받는 ‘문지기’ 역할을 하고, 내부에서 실제 비즈니스 로직을 처리하는 Spring Boot 애플리케이션(8080 포트)으로 요청을 안전하게 전달해주는 **리버스 프록시(Reverse Proxy)**로 주로 활약합니다.
  • 왜 필요한가?:
    • 성능 및 부하 분산: 정적 파일(이미지, CSS 등)은 Nginx가 빠르게 처리하고, 동적인 API 요청만 Spring Boot 서버로 넘겨 부하를 줄입니다. 여러 대의 Spring Boot 서버로 요청을 분산(로드 밸런싱)할 수도 있습니다.
    • 보안: 애플리케이션 서버(WAS)를 외부에 직접 노출하지 않아 보안이 향상됩니다.
    • 무중단 배포: 배포 시 Nginx가 트래픽을 구버전 앱에서 신버전 앱으로 점진적으로 전환시켜 서비스 중단 없이 배포가 가능합니다.

17. 원격 접속과 보안 (SSH)
#

서버실(데이터 센터)에 직접 가지 않고도, 집이나 카페에서 서버 컴퓨터를 제어하는 기술입니다.

17.1. SSH (Secure Shell) 개념
#

  • 리눅스 서버 관리의 표준입니다. 모든 데이터가 암호화되어 전송되므로 안전합니다.
  • SSH Server: 제어 받는 컴퓨터(서버)에 설치 (openssh-server).
  • SSH Client: 제어 하는 컴퓨터(내 맥북/노트북)에 설치.

17.2. 접속 방법
#

  • 기본 명령어: ssh [사용자ID]@[서버IP]
    ssh ubuntu@192.168.0.65
  • 포트 지정: 보안을 위해 기본 포트(22)를 다른 번호로 바꾼 경우 -p 옵션을 사용합니다.
    ssh -p 2222 ubuntu@192.168.0.65

백엔드 개발자 Tip: 비밀번호 대신 ‘키 파일(.pem)’ 사용 실무(AWS 등)에서는 보안상 비밀번호 로그인을 막아두고, **SSH 키 파일(Key Pair)**을 사용합니다.

# -i 옵션으로 키 파일 지정
ssh -i my-key.pem ubuntu@3.12.34.56

이때 키 파일의 권한은 반드시 400(chmod 400 my-key.pem)이어야 합니다. (너무 공개된 권한이면 SSH가 접속을 거부함)


18. 포트, 방화벽과 외부 접속
#

“서버를 켰는데 외부에서 접속이 안 돼요!“라는 문제의 99%는 이 부분 설정 문제입니다.

18.1. 포트(Port)란?
#

IP가 컴퓨터(건물)를 찾는 주소라면, 포트는 그 안의 **프로그램(방 번호)**을 찾는 주소입니다. (0 ~ 65535번)

  • 주요 포트 (Well-Known Ports):

    • 22: SSH (원격 접속)
    • 80: HTTP (웹)
    • 443: HTTPS (보안 웹)
    • 3306: MySQL / MariaDB
    • 5432: PostgreSQL
    • 6379: Redis
    • 8080: Tomcat, Spring Boot 등 WAS의 대체/개발용 포트
  • 하나의 포트는 하나의 프로그램만 사용할 수 있습니다. (이미 80번을 Apache가 쓰고 있다면 Nginx는 80번을 쓸 수 없음)

  • 포트 점유 확인: 특정 포트(예: 8080)를 누가 쓰는지 알고 싶다면 sudo ss -ltnp | grep 8080 또는 sudo lsof -i :8080으로 확인합니다. Spring Boot 서버가 안 뜨거나 포트 충돌이 날 때 가장 먼저 확인하세요.

18.2. 방화벽 (Firewall)
#

서버로 들어오고 나가는 네트워크 트래픽을 통제하는 보안 시스템으로, 허가된 포트가 아니면 모든 접근을 차단합니다. 외부 접속 문제의 또 다른 주요 원인입니다.

  • ufw (Ubuntu 기본 방화벽) 명령어:
    # 방화벽 활성화
    sudo ufw enable
    
    # 방화벽 상태 확인
    sudo ufw status
    
    # 특정 포트 허용 (Nginx, SSH, Spring Boot App)
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw allow 22/tcp
    sudo ufw allow 8080/tcp
    
    # 규칙 삭제
    sudo ufw delete allow 8080/tcp

18.3. 포트 포워딩 (Port Forwarding)
#

공유기를 사용하는 환경(집, 사무실)에서 외부 접속을 가능하게 해주는 기술입니다.

  • 문제: 외부에서는 공유기의 공인 IP까지만 접근 가능하고, 내 컴퓨터의 사설 IP(192.168...)로는 들어올 수 없습니다.
  • 해결: 공유기에게 이정표를 세워줍니다.
    • “외부에서 공인 IP의 9000번 포트로 들어오면 -> 내 컴퓨터(192.168.0.65)의 8080번 포트로 연결해줘!”

18.4. 설정 및 테스트 단계
#

  1. 방화벽 확인: 서버 자체의 방화벽(ufw 등)이 원하는 포트(예: 80, 8080)를 허용하는지 확인합니다.
  2. 공유기 설정 페이지 접속: 보통 192.168.0.1 등의 주소로 접속 (Default Gateway 확인).
  3. NAT/포트포워딩 설정:
    • 내부 IP: 서버 컴퓨터의 사설 IP
    • 외부 포트: 9000 (외부에 노출할 번호)
    • 내부 포트: 8080 (실제 서버 프로그램 포트)
  4. 접속 테스트: 스마트폰(LTE/5G 모드)에서 http://[내 공인IP]:9000 접속 시도. (Wi-Fi 환경에서는 테스트가 제대로 안 될 수 있음)

백엔드 개발자 Tip: 클라우드와 Docker

  • 클라우드 (AWS, GCP, Azure 등): 클라우드 환경에서는 공유기 대신 **‘보안 그룹(Security Group)’**이나 **‘방화벽 규칙(Firewall Rules)’**에서 인바운드(Inbound) 규칙을 설정해줘야 외부 접속이 가능합니다. (예: 80, 443, 22, 8080 포트 허용)
  • Docker: docker run -p 8080:8080 my-app 에서 -p 옵션이 바로 포트 포워딩 개념입니다. “호스트(리눅스) 서버의 8080번 포트로 들어온 요청을 -> 컨테이너 내부의 8080번 포트로 연결하라"는 의미입니다.