Introduction

본 playground 코스는 합성생물학 ⋅ 인공진화공학 연구실의 개발 환경 및 인프라 접근을 위한 기본기 학습을 목표로 합니다.

🎯 학습 목표

현대적인 연구 환경에서 요구되는 핵심 기술들을 체계적으로 학습하여 연구 생산성을 극대화합니다:

  • 재현 가능한 연구 환경 구축 및 관리
  • 효율적인 협업 도구 활용법 습득
  • 기본 보안 개념과 실무 적용
  • 생물정보학 도구와 분석 파이프라인 구성
  • 연구 데이터 관리와 문서화 전략

지원 체계

  • 슬랙 채널: #playground-support 실시간 질문/답변
  • 주간 세미나: 격주 수요일 학습 내용 공유 및 토론

중요: 이 과정은 세부적인 기초 내용까지 모두 다루지 않으므로, 과제와 추가 학습을 통해 학습자가 스스로 채워나가야 합니다.

📚 과정 구성

Part I: Foundation

현대적인 개발 환경의 기초가 되는 필수 도구들을 학습합니다.

Chapter 1: Linux & Shell Fundamentals

  • Unix 철학과 명령줄 인터페이스의 이해
  • 파일 시스템과 권한 관리
  • 프로세스 관리와 모니터링
  • 텍스트 처리 도구 활용
  • Shell 스크립팅 기초

Chapter 2: Development Environment

  • 프로그래밍 패러다임과 함수형 접근법
  • Nix를 통한 재현 가능한 환경 구축
  • 패키지 관리와 의존성 해결
  • 다양한 플랫폼별 환경 관리

Chapter 3: Version Control & Collaboration

  • Git을 활용한 버전 관리
  • 브랜치 전략과 협업 워크플로우
  • 연구 데이터 버전 관리
  • 코드 리뷰와 협업 도구

Chapter 4: Security & Cryptography

  • 암호학 기초와 실무 적용
  • SSH 키 관리와 안전한 접속
  • 데이터 암호화와 비밀 관리
  • 연구 데이터 보안

Part II: Scientific Computing

과학 연구에 특화된 컴퓨팅 도구와 방법론을 학습합니다.

Chapter 5-7: Python & Data Science

  • Python 과학 생태계
  • 데이터 조작과 시각화
  • 통계 분석과 머신러닝 기초

Part III: Bioinformatics 🧬

생물정보학 특화 도구와 분석 방법을 심화 학습합니다.

Chapter 8-12: 생물정보학 전문 과정

  • 생물학적 데이터 형식과 처리
  • 서열 분석과 정렬 알고리즘
  • 유전체학과 전사체학 분석
  • 합성생물학 도구와 설계 자동화
  • 인공진화공학과 적응 진화 분석

Part IV: Advanced Applications

연구 환경의 고급 활용법과 자동화를 다룹니다.

Chapter 13-15: 연구 워크플로우와 인프라

  • 연구 프로젝트 구조화
  • 고성능 컴퓨팅 활용
  • CI/CD를 통한 연구 자동화

🚀 학습 방법

실습 중심 학습

각 챕터는 이론과 실습이 균형 있게 구성되어 있습니다:

  1. 개념 학습: 핵심 개념과 원리 이해
  2. 명령어 실습: 단계별 실습 예제
  3. 프로젝트 적용: 실제 연구 시나리오 적용
  4. 문제 해결: 실무에서 발생하는 문제 해결

Practice Section

각 섹션 마지막에는 학습한 내용을 직접 실습할 수 있는 예제가 포함되어 있습니다: 매 섹션의 실습 예제 결과는 과제의 일부이며, Pull-Request 방식으로 제출되어 평가됩니다.

순차적 학습 권장

  • Foundation 파트는 모든 학습자에게 필수
  • Scientific Computing과 Bioinformatics는 연구 분야에 따라 선택적 학습
  • Advanced Applications는 인프라 관리자나 고급 사용자 대상

🛠️ 사전 요구사항

필수 배경 지식

  • 기본적인 컴퓨터 사용 경험
  • 텍스트 파일 편집 능력
  • 프로그래밍 언어 경험 (Python, R 등 중 하나 이상)

권장 배경 지식

  • 생물학 기초 지식 (생물정보학 파트)
  • 통계학 기초 (데이터 분석 파트)
  • Linux/Unix 사용 경험

시스템 요구사항

1. Install Nix

  • 터미널에서 다음 명령어로 Nix 를 설치합니다.

  • Windows 사용자의 경우, WSL2 를 먼저 설치한 후 Ubuntu Terminal 에서 아래 코드를 실행해야 합니다.

    curl -L https://nixos.org/nix/install | sh
    

2. Source your shell or restart the Terminal

  • 설치가 완료되면 아래의 코드를 실행하거나 터미널을 재시작하여 shell 환경을 재구성합니다.

    source ~/.nix-profile/etc/profile.d/nix.sh
    
  • 설치 확인

    nix --version
    

🛠️ 실습 환경 구성

실습을 위해 playground repository 를 clone 하여야 합니다.

git clone https://github.com/sbee-lab/playground

모든 실습은 clone 한 repository 의 ./practices directory 에서 수행됩니다.

# clone 한 repository 로 이동
cd playground
# 실습을 위한 directory 로 이동
cd ./practice

실습을 위한 환경은 Chapter 별 Nix 환경에서 진행됩니다:

nix develop .#chapter1
nix develop .#chapter2
nix develop .#chapter3

이제 본격적으로 PartI: Foundation 부터 학습을 진행합시다.

다음: PartI: Foundation

Part I: Foundation

현대적인 연구 환경 구축에 필요한 핵심 기술들을 학습합니다.

🎯 Part I 학습 목표

재현 가능성(Reproducibility)효율적인 협업(Collaboration) 을 통해 모든 연구자가 동일하고 안전한 환경에서 작업할 수 있도록 합니다.

graph TD
    A[Linux & Shell] --> B[Development Environment]
    B --> C[Version Control]
    C --> D[Security & Cryptography]

    A --> A1[Unix Philosophy]
    A --> A2[File System]
    A --> A3[Process Management]

    B --> B1[Programming Paradigms]
    B --> B2[Nix Package Manager]
    B --> B3[Environment Management]

    C --> C1[Git Version Control]
    C --> C2[Collaboration Workflow]
    C --> C3[Research Data Versioning]

    D --> D1[Cryptography Fundamentals]
    D --> D2[Key Management]
    D --> D3[Data Security]

📚 Chapter 구성

Chapter 1: Linux & Shell Fundamentals

"모든 것의 기초가 되는 명령줄 환경"

연구 환경의 기반이 되는 Linux/Unix 시스템과 Shell 환경을 마스터합니다. 생물정보학과 데이터 분석에서 필수적인 명령줄 도구들을 학습합니다.

핵심 도구: bash, grep, awk, sed, find, xargs

Chapter 2: Development Environment

"재현 가능한 연구 환경 구축"

함수형 패키지 관리자 Nix를 통해 완전히 재현 가능한 개발 환경을 구축합니다. 의존성 충돌 없이 다양한 도구를 격리된 환경에서 사용하는 방법을 학습합니다.

핵심 도구: nix, nix-shell, flakes, direnv

Chapter 3: Version Control & Collaboration

"연구 코드와 데이터의 체계적 관리"

Git을 활용한 버전 관리와 GitHub을 통한 협업 워크플로우를 학습합니다. 연구 데이터의 버전 관리와 재현 가능한 분석 파이프라인 구축 방법을 다룹니다.

핵심 도구: git, github, git-lfs, dvc

Chapter 4: Security & Cryptography

"연구 데이터와 시스템 보안"

연구 환경에서 필요한 기본적인 보안 개념과 암호화 도구를 학습합니다. SSH 키 관리, 데이터 암호화, 비밀 정보 관리 방법을 다룹니다.

핵심 도구: ssh, gpg, age, sops


시작하기: Chapter 1: Linux & Shell Fundamentals에서 첫 번째 단계를 시작하세요

Chapter 1: Linux & Shell Fundamentals

연구 환경의 기초가 되는 Linux/Unix 시스템과 Shell 환경을 마스터합니다. 생물정보학과 데이터 분석에서 필수적인 명령줄 도구들을 실습 중심으로 학습합니다.

학습 목표

  • Unix 철학과 명령줄 인터페이스 이해
  • 파일 시스템 구조와 권한 관리
  • 프로세스 모니터링과 관리
  • 텍스트 처리 도구 활용
  • Shell 스크립팅을 통한 작업 자동화

전제 조건

# Nix 환경 활성화
nix develop .#chapter01

# 실습 디렉토리로 이동
cd practice/chapter01

목차


Chapter 1 완료 후 기대 효과

  • 시스템 탐색: Linux 파일 시스템에서 자유자재로 이동하고 파일 조작
  • 데이터 처리: 대용량 텍스트 파일을 효율적으로 처리하고 분석
  • 작업 관리: 장시간 실행되는 분석 작업을 안정적으로 관리
  • 자동화: 반복적인 작업을 스크립트로 자동화하여 생산성 향상

이제 Chapter 1.1 Unix Philosophy 부터 시작해봅시다

1.1 Unix Philosophy & Command Line

핵심 개념

Unix는 "작은 도구들을 연결하여 복잡한 작업을 수행한다"는 철학을 기반으로 설계되었습니다. 각 명령어는 하나의 기능을 잘 수행하며, 파이프(|)로 연결하여 강력한 데이터 처리 파이프라인을 구성할 수 있습니다.

기본 파일 시스템 탐색

파일 시스템 탐색은 모든 작업의 기초입니다. 현재 위치를 파악하고, 파일과 디렉토리를 확인하며, 원하는 위치로 이동하는 것이 첫 번째 단계입니다.

# 현재 위치 확인
pwd                     # Print Working Directory

# 파일과 디렉토리 목록 보기
ls                      # 기본 목록
ls -l                   # 상세 정보 (권한, 크기, 날짜)
ls -la                  # 숨김 파일(.으로 시작) 포함
ls -lh                  # 파일 크기를 사람이 읽기 쉽게 (1K, 2M, 3G 등)
ls -lt                  # 수정 시간 순으로 정렬 (최근 파일이 위에)

# 디렉토리 이동
cd /absolute/path       # 절대 경로로 이동
cd relative/path        # 상대 경로로 이동
cd ~                    # 홈 디렉토리로 이동
cd -                    # 이전 디렉토리로 이동 (매우 유용)
cd ..                   # 상위 디렉토리로 이동
cd ../..                # 두 단계 위로 이동

파일과 디렉토리 조작

파일과 디렉토리 생성, 복사, 이동, 삭제는 연구 데이터 관리의 핵심 작업입니다. 특히 생물정보학에서는 대량의 데이터 파일을 체계적으로 관리해야 합니다.

# 디렉토리 생성
mkdir new_directory                    # 단일 디렉토리 생성
mkdir -p path/to/nested/directory     # 중간 경로까지 모두 생성

# 파일 생성
touch filename.txt                     # 빈 파일 생성
echo "Hello World" > filename.txt      # 내용과 함께 파일 생성

# 파일 복사
cp source.txt destination.txt          # 파일 복사
cp -r source_dir dest_dir             # 디렉토리 전체 복사 (recursive)
cp *.fasta backup/                    # 패턴 매칭으로 여러 파일 복사

# 파일 이동과 이름 변경
mv old_name.txt new_name.txt          # 파일 이름 변경
mv file.txt /new/location/            # 파일 이동
mv *.log logs/                        # 여러 파일을 특정 디렉토리로 이동

# 파일과 디렉토리 삭제
rm filename.txt                       # 파일 삭제
rm -i filename.txt                    # 삭제 전 확인 (interactive)
rm -r directory                       # 디렉토리와 내용 모두 삭제
rm -rf directory                      # 강제 삭제 (주의 필요!)

파일 내용 확인

연구 데이터 파일의 내용을 확인하는 것은 분석 전 필수 단계입니다. 파일 크기에 따라 적절한 명령어를 선택해야 합니다.

# 전체 내용 보기
cat filename.txt                      # 전체 내용을 한 번에 출력
less filename.txt                     # 페이지 단위로 보기 (q로 종료)
more filename.txt                     # 페이지 단위로 보기 (아래로만 스크롤)

# 파일의 일부분만 보기
head filename.txt                     # 처음 10줄
head -n 20 filename.txt              # 처음 20줄
tail filename.txt                     # 마지막 10줄
tail -n 50 filename.txt              # 마지막 50줄
tail -f logfile.txt                   # 실시간으로 추가되는 내용 모니터링

# 줄 번호와 함께 보기
nl filename.txt                       # 줄 번호 추가
cat -n filename.txt                   # 줄 번호와 함께 전체 출력

# 파일 정보 확인
wc filename.txt                       # 줄 수, 단어 수, 문자 수
wc -l *.fasta                        # 여러 파일의 줄 수만
file filename.txt                     # 파일 타입 확인

패턴 매칭과 와일드카드

생물정보학에서는 동일한 형식의 파일들을 일괄 처리하는 경우가 많습니다. 와일드카드를 효과적으로 사용하면 작업 효율성을 크게 높일 수 있습니다.

# 기본 와일드카드
*                    # 모든 문자 (0개 이상)
?                    # 한 개의 문자
[abc]                # a, b, c 중 하나
[a-z]                # a부터 z까지 중 하나
[0-9]                # 0부터 9까지 중 하나
[!0-9]               # 숫자가 아닌 문자

# 실제 사용 예시
ls *.txt             # 모든 .txt 파일
ls *.fasta           # 모든 FASTA 파일
ls data_*.csv        # data_로 시작하는 모든 .csv 파일
ls file?.txt         # file + 한 글자 + .txt (file1.txt, filea.txt 등)
ls sample_[0-9]*.fastq   # sample_ + 숫자로 시작하는 FASTQ 파일

# 중괄호 확장 (Brace Expansion)
mkdir {data,results,scripts,docs}     # 여러 디렉토리 한 번에 생성
cp file.txt{,.backup}                 # file.txt를 file.txt.backup으로 복사
echo {1..10}                          # 1부터 10까지 숫자 나열
echo sample_{001..100}.fasta          # sample_001.fasta부터 sample_100.fasta까지

명령어 조합과 파이프라인

Unix의 진정한 힘은 작은 도구들을 조합할 때 나타납니다. 파이프라인을 통해 복잡한 데이터 처리를 단순한 명령어들의 조합으로 해결할 수 있습니다.

# 기본 파이프라인
command1 | command2                   # command1의 출력을 command2의 입력으로
command1 | command2 | command3        # 여러 명령어 연결

# 실제 사용 예시
ls -la | grep "\.fasta$"             # FASTA 파일만 필터링
cat file.txt | sort | uniq           # 파일 내용 정렬 후 중복 제거
ls -la | wc -l                       # 현재 디렉토리의 파일 개수
find . -name "*.log" | head -5        # 로그 파일 중 처음 5개만

# 복잡한 파이프라인 예시
ls -la | grep "^-" | awk '{print $5, $9}' | sort -n   # 파일 크기별로 정렬
cat sequences.fasta | grep "^>" | wc -l                # FASTA 파일의 서열 개수

도구 위치와 정보 확인

시스템에 설치된 도구들의 위치와 정보를 확인하는 것은 스크립트 작성이나 문제 해결 시 중요합니다.

# 명령어 위치 찾기
which python          # python 실행 파일의 경로
whereis python         # python 관련 파일들의 위치 (실행파일, 매뉴얼 등)
type python            # 명령어의 타입 (내장, 함수, 별칭, 실행파일)

# 시스템 정보
whoami                 # 현재 사용자 이름
hostname               # 시스템 이름
uname -a               # 시스템 전체 정보
date                   # 현재 날짜와 시간

# 도움말 보기
man ls                 # ls 명령어의 매뉴얼
ls --help             # 간단한 도움말
info ls                # 상세한 정보 페이지

Practice Section: 연구 프로젝트 구조 생성

실습 전, 개발환경에 먼저 입장합니다. 실습 환경 구성을 참고하세요.

# 실습 디렉토리로 진입 (playground/practice/chapter01)
cd ./practice/chapter01

# 실습 환경 진입
nix develop .#chapter01

이번 실습을 통해 생성되어야 할 최종 outputs 은 다음과 같습니다.

chapter01
└── bioproject
    ├── data
    │   ├── backup
    │   │   ├── metadata.csv
    │   │   └── sequences.fasta
    │   ├── processed
    │   │   └── sequences_working.fasta
    │   └── raw
    │       ├── metadata.csv
    │       └── sequences.fasta
    └── results
        └── report.txt

6 directories, 6 files

실습 1: 프로젝트 구조 생성

# 현재 디렉토리 위치 확인하기
pwd

# 생물정보학 연구 프로젝트 디렉토리 생성
mkdir -p bioproject/{data/{raw,processed,backup},results}

# 현재 구조 확인
cd ./bioproject

ls -la

# tree 로 전체 구조 확인
tree

## find all directories
find . -type d | sort

실습 2: 샘플 데이터 파일 생성

cd ./data/raw

# 간단한 FASTA 파일 생성
cat > sequences.fasta << 'EOF'
>seq1_Ecoli
ATGAAACGCATTAGCACCACCATTACCACCACCATCACCATTACCACAGGTAACGGTGCG
GGCTGACGCGTACAGGAAACACAGAAAAAAGCCCGCACCTGACAGTGCGGGCTTTTTTT
>seq2_Bsubtilis
ATGAGCGAACAAGCCATTGCGAATGTTCTGAAAGATGCCGAAAACATCGCCCGTGAAATC
GTTGAAGAAGCGAACACCATGCCGGATAAAGTCGATATCGACAAACTGAAAGCGCTGCTG
>seq3_Saureus
ATGAAAAAATTAGTTTTAGCAGGTGCAGGTGCAGGTGCAGGTGCAGGTGCAGGTGCAGGT
GCGGGTATCGATAAAGCGCAAATCGATCTGGAAATGCTGGTTAAAGATGAAAAGAACTAT
EOF

# 메타데이터 파일 생성
cat > metadata.csv << 'EOF'
sample_id,organism,condition,replicate
seq1,E.coli,control,1
seq2,B.subtilis,treatment,1
seq3,S.aureus,control,2
EOF

# 잘 생성되었는지 확인
ls -la
cat sequences.fasta
cat metadata.csv

실습 3: 기본 파일 분석 보고서 작성

# FASTA 파일 기본 분석
# 1. 서열 개수 ('>' 의 개수와 동일함)
echo "The number of sequences: $(grep -c '^>' sequences.fasta)" > ../../result/report.txt

# 2. 총 라인 수
echo "The number of total lines: $(wc -l < sequences.fasta)" >> ../../result/report.txt

# 3. 서열 이름 확인
grep '^>' sequences.fasta

# 메타데이터 분석
# 1. 총 샘플 수
echo "The number of total samples: $(tail -n +2 metadata.csv | wc -l)" >> ../../result/report.txt

# 2. 유기체별 분포
tail -n +2 metadata.csv | cut -d',' -f2 | sort | uniq -c

실습 4: 파일 조작과 백업

# 백업 디렉토리로 파일 복사
cp *.fasta ../backup/
cp metadata.csv ../backup/metadata.csv

# 또는 날짜를 추가할 수도 있음
cp metadata.csv ../backup/metadata_$(date +%Y%m%d).csv

# 파일 패턴 매칭 연습
ls *.{fasta,csv}

# 파일 정리
cp sequences.fasta ../processed/sequences_working.fasta

# 파일 삭제
rm ../backup/metadata_$(date +%Y%m%d).csv

실습 5: 명령어 조합 연습

# 파이프라인을 이용한 데이터 분석

# 가장 긴 서열 라인 찾기
echo "The most long sequence: $(grep -v '^>' sequences.fasta | awk '{print length, $0}' | sort -nr | head -1)" >> ../../result/report.txt

# 파일 크기별 정렬
ls -la | grep -v '^d' | sort -k5 -nr

# 디렉토리 구조 요약
cd ../../..
find ./bioproject -type f | wc -l | xargs echo "총 파일 수:"
find ./bioproject -type d | wc -l | xargs echo "총 디렉토리 수:"

핵심 정리

자주 사용하는 명령어 조합

# 파일 탐색과 정보
ls -la | grep -E "(fasta|csv)"         # 특정 파일 타입만 보기
find . -name "*.txt" | head -10        # 텍스트 파일 처음 10개
du -sh */ | sort -hr                   # 디렉토리 크기별 정렬

# 데이터 확인
head -5 file.txt && tail -5 file.txt   # 파일 앞뒤 확인
wc -l *.fasta                          # 여러 파일의 라인 수

효율적인 작업 패턴

  1. 탐색 → 확인 → 작업: lshead/catcp/mv
  2. 패턴 활용: 와일드카드로 여러 파일 동시 처리
  3. 파이프라인: 작은 명령어들을 연결하여 복잡한 작업 수행
  4. 백업 습관: 중요한 작업 전 항상 백업

One-step more

최근에는 메모리 안정성과 속도 면에서 각광받고 있는 Rust 로 쓰여진 프로그램 ripgrep sd fd 등이 각광받고 있습니다. 사용자 경험 면에서도 명령어 작성이 편리하여, 활용을 고려해보세요


다음: 1.2 File System & Permissions

1.2 File System & Permissions

핵심 개념

Linux 파일 시스템은 계층적 구조로 조직화되어 있으며, 모든 파일과 디렉토리에는 소유권과 권한이 설정되어 있습니다. 권한 시스템은 데이터 보안과 시스템 안정성을 보장하는 핵심 메커니즘입니다.

Linux 파일 시스템 구조 (FHS)

Linux 파일 시스템은 Filesystem Hierarchy Standard(FHS)를 따라 구성됩니다. 각 디렉토리는 특정한 용도를 가지며, 이를 이해하면 시스템을 더 효과적으로 활용할 수 있습니다.

/                    # 루트 디렉토리 (모든 것의 시작점)
├── bin/             # 필수 실행 파일 (ls, cp, mv 등)
├── etc/             # 시스템 설정 파일
├── home/            # 사용자 홈 디렉토리
│   └── username/    # 개별 사용자 디렉토리
├── tmp/             # 임시 파일 (재부팅 시 삭제될 수 있음)
├── usr/             # 사용자 프로그램
│   ├── bin/         # 일반 사용자 실행 파일
│   ├── lib/         # 공유 라이브러리
│   └── local/       # 로컬 설치 프로그램
└── var/             # 변경되는 데이터
    ├── log/         # 시스템 로그 파일
    └── tmp/         # 영구적인 임시 파일

경로 표현 방식

파일과 디렉토리의 위치를 나타내는 방법에는 절대 경로와 상대 경로가 있습니다. 각각의 특성을 이해하고 상황에 맞게 사용해야 합니다.

# 절대 경로: 루트(/)부터 시작하는 전체 경로
/home/user/bioproject/data/sequences.fasta
/usr/bin/python
/etc/passwd

# 상대 경로: 현재 위치부터 시작하는 경로
./data/sequences.fasta          # 현재 디렉토리의 data 하위
../scripts/analysis.py          # 상위 디렉토리의 scripts 하위
../../backup/                   # 두 단계 위의 backup

# 특수 경로 표현
~                               # 현재 사용자의 홈 디렉토리
~/bioproject/                   # 홈 디렉토리의 bioproject
~username/shared/               # 다른 사용자의 shared 디렉토리

파일 권한 시스템

Linux의 권한 시스템은 각 파일과 디렉토리에 대해 소유자(user), 그룹(group), 기타(others)의 세 가지 주체에 대한 읽기(read), 쓰기(write), 실행(execute) 권한을 관리합니다.

# ls -l 출력 해석
$ ls -l filename.txt
-rw-r--r-- 1 user group 1024 Jan 15 10:30 filename.txt
 |||||||||| | |    |     |    |           |
 |||||||++-- 기타 사용자 권한 (r--)
 ||||+++---- 그룹 권한 (r--)
 |+++------- 소유자 권한 (rw-)
 +---------- 파일 타입 (- = 일반파일, d = 디렉토리, l = 링크)
             | |    |     |    |           |
             | |    |     |    |           +-- 파일명
             | |    |     |    +-- 수정 날짜/시간
             | |    |     +-- 파일 크기 (바이트)
             | |    +-- 그룹명
             | +-- 소유자명
             +-- 링크 수

권한의 의미는 파일과 디렉토리에서 다르게 해석됩니다:

권한파일에서의 의미디렉토리에서의 의미
r (read)파일 내용 읽기디렉토리 목록 보기 (ls)
w (write)파일 내용 수정/삭제파일 생성/삭제 가능
x (execute)파일 실행디렉토리 진입 가능 (cd)

권한 변경 명령어

권한을 변경하는 방법에는 숫자 표기법과 기호 표기법이 있습니다. 각각의 장단점을 이해하고 상황에 맞게 사용해야 합니다.

# 숫자 표기법 (8진수)
# r=4, w=2, x=1로 계산하여 합산
chmod 755 script.sh             # rwxr-xr-x (소유자: 모든 권한, 그룹/기타: 읽기+실행)
chmod 644 data.txt              # rw-r--r-- (소유자: 읽기+쓰기, 그룹/기타: 읽기만)
chmod 600 private.key           # rw------- (소유자만 읽기+쓰기)
chmod 777 shared_dir            # rwxrwxrwx (모든 사용자 모든 권한 - 보안상 위험)

# 기호 표기법
chmod u+x script.sh             # 소유자(user)에게 실행 권한 추가
chmod g-w file.txt              # 그룹(group)에서 쓰기 권한 제거
chmod o-r private.txt           # 기타(others)에서 읽기 권한 제거
chmod a+r public.txt            # 모든 사용자(all)에게 읽기 권한 추가

# 복합 권한 설정
chmod u=rw,g=r,o= secret.txt    # 소유자:읽기+쓰기, 그룹:읽기만, 기타:권한없음
chmod -R 755 directory/         # 디렉토리와 모든 하위 파일에 재귀적으로 적용

소유권 관리

파일과 디렉토리의 소유자와 그룹을 변경하는 것도 권한 관리의 중요한 부분입니다.

# 소유자 변경
chown newuser file.txt                  # 소유자만 변경
chown newuser:newgroup file.txt         # 소유자와 그룹 모두 변경
chown :newgroup file.txt                # 그룹만 변경
chown -R user:group directory/          # 재귀적으로 변경

# 그룹만 변경 (chgrp 사용)
chgrp newgroup file.txt
chgrp -R research_team data/

# 현재 사용자와 그룹 확인
id                                      # 현재 사용자의 UID, GID, 소속 그룹 정보
groups                                  # 현재 사용자가 속한 그룹들
whoami                                  # 현재 사용자명

고급 파일 조작

파일의 성격과 연결 관계를 이해하고 활용하는 것은 효율적인 파일 관리에 중요합니다.

# 파일 타입과 정보 확인
file filename                   # 파일 타입 확인 (텍스트, 바이너리, 실행파일 등)
file *.fasta                   # 여러 파일의 타입 확인
stat filename                  # 상세한 파일 정보 (크기, 권한, 타임스탬프, inode 등)

# 심볼릭 링크 생성과 활용
ln -s /path/to/original.txt symlink.txt              # 심볼릭 링크 생성
ln -s ../data/sequences.fasta current_sequences      # 상대 경로로 링크
readlink symlink.txt                                 # 링크가 가리키는 원본 파일 확인
ls -la | grep "^l"                                  # 심볼릭 링크만 찾기

# 하드 링크 (같은 inode를 공유)
ln original.txt hardlink.txt             # 하드 링크 생성
ls -li                                   # inode 번호와 함께 출력 (같은 파일은 같은 inode)

파일 검색

대용량 연구 데이터에서 원하는 파일을 찾는 것은 자주 발생하는 작업입니다. find 명령어는 매우 강력한 검색 도구입니다.

# 이름으로 검색
find /path -name "pattern"              # 정확한 패턴 매칭
find . -name "*.fasta"                  # 현재 디렉토리에서 FASTA 파일 찾기
find . -iname "*.FASTA"                 # 대소문자 무시하고 검색

# 타입으로 검색
find . -type f -name "*.txt"            # 일반 파일 중에서 .txt 파일
find . -type d -name "*data*"           # 디렉토리 중에서 data가 포함된 것
find . -type l                          # 심볼릭 링크만 찾기

# 크기로 검색
find . -size +1M                        # 1MB보다 큰 파일
find . -size -100k                      # 100KB보다 작은 파일
find . -empty                           # 빈 파일 찾기

# 시간으로 검색
find . -mtime -7                        # 7일 이내에 수정된 파일
find . -atime +30                       # 30일 이상 접근하지 않은 파일
find . -newer reference_file             # 참조 파일보다 최근에 수정된 파일

# 권한으로 검색
find . -perm 644                        # 정확히 644 권한인 파일
find . -perm -644                       # 최소 644 권한 이상인 파일
find . -perm /u+w                       # 소유자 쓰기 권한이 있는 파일

# 소유자로 검색
find . -user username                   # 특정 사용자가 소유한 파일
find . -group groupname                 # 특정 그룹이 소유한 파일

# 검색 결과로 작업 수행
find . -name "*.tmp" -delete            # 임시 파일 모두 삭제
find . -name "*.fasta" -exec ls -l {} \;  # FASTA 파일의 상세 정보 출력
find . -type f -exec chmod 644 {} \;    # 모든 파일을 644 권한으로 변경

Practice Section: 연구 데이터 권한 관리

이번 실습을 통해 생성되어야 할 최종 outputs 은 다음과 같습니다.

[drwxr-xr-x]  chapter01
└── [drwxr-xr-x]  bioproject
    ├── [lrwxr-xr-x]  current_data -> data/raw/sequences.fasta
    ├── [lrwxr-xr-x]  current_metadata -> data/raw/metadata.csv
    ├── [drwxr-x---]  data
    │   ├── [drwxr-xr-x]  backup
    │   │   ├── [-rw-------]  metadata.csv
    │   │   └── [-rw-------]  sequences.fasta
    │   ├── [drwxr-xr-x]  processed
    │   │   └── [-rw-r--r--]  sequences_working.fasta
    │   └── [drwx------]  raw
    │       ├── [-rw-r--r--]  metadata.csv
    │       └── [-rw-r--r--]  sequences.fasta
    └── [drwxr-xr-x]  results
        └── [-rw-r--r--]  report.txt

6 directories, 8 files

실습 1: 프로젝트 권한 구조 설정

# 실습 환경 진입
nix develop .#chapter01

# 이전 섹션에서 생성한 ./bioproject 에서 이어서 작업합니다.
# (./playground/practice/chapter01/bioproject)
cd ./bioproject


# 현재 권한 상태 확인
ls -la
ls -la data/

# 디렉토리별 적절한 권한 설정
chmod 755 .                     # 프로젝트 루트: 일반적인 디렉토리 권한
chmod 750 data/                 # 데이터: 그룹만 접근 가능
chmod 700 data/raw/             # 원시 데이터: 소유자만 접근
chmod 755 results/              # 결과: 공유 가능

# 권한 설정 확인
ls -la

실습 2: 파일별 보안 등급 적용

cd ./data/raw

# 민감한 데이터 보호
chmod 600 metadata.csv          # 메타데이터: 소유자만 읽기/쓰기

# 서열 데이터 공유 설정
chmod 644 sequences.fasta       # FASTA: 모든 사용자 읽기 가능

# 백업 파일 보호
chmod 600 ../backup/* 2>/dev/null || echo "백업 파일 없음"

# 권한 확인
echo "=== 파일 권한 현황 ==="
ls -la | awk '{print $1, $9}'

실습 3: 심볼릭 링크 활용

cd ../..

# 자주 사용하는 파일에 대한 바로가기 생성
ln -s data/raw/sequences.fasta current_data
ln -s data/raw/metadata.csv current_metadata

# 링크 확인
ls -la | grep "^l"
readlink current_data

실습 4: 파일 검색 연습

# 모든 데이터 파일 찾기
find . -name "*.fasta" -o -name "*.csv"

핵심 정리

권한 설정 모범 사례

# 일반적인 권한 패턴
chmod 755 directories/        # 디렉토리: rwxr-xr-x
chmod 644 data_files          # 데이터 파일: rw-r--r--
chmod 755 executable_scripts  # 실행 스크립트: rwxr-xr-x
chmod 600 private_files       # 개인 파일: rw-------

# 보안 검사
find . -perm -002 -type f      # 모든 사용자 쓰기 가능한 파일 찾기
find . -perm 777               # 모든 권한이 열린 파일/디렉토리

자주 사용하는 검색 패턴

# 연구 데이터 관리용 검색
find . -name "*.fasta" -exec ls -lh {} \;     # FASTA 파일 크기 확인
find . -type f -size +100M                    # 대용량 파일 찾기
find . -mtime +30 -name "*.log"               # 오래된 로그 파일
find . -empty -type f -delete                 # 빈 파일 정리

다음: 1.3 Process Management

1.3 Process Management

CAUTION

MacOS 환경에서는 Linux 와 process 관리가 달라 일부 프로그램이 지원되지 않습니다. 향후 학습 환경을 VM 으로 업데이트할 예정이므로, 지원 전까지는 VM 을 별도로 사용하시거나 Docker 사용을 권장합니다.

Windows 에서는 WSL 이 Linux 이므로 동일하게 동작할 것입니다.

핵심 개념

프로세스는 실행 중인 프로그램의 인스턴스입니다. 생물정보학 연구에서는 장시간 실행되는 분석 작업이 많기 때문에 프로세스 모니터링, 제어, 백그라운드 실행 관리가 매우 중요합니다.

프로세스 기본 개념

모든 프로세스는 고유한 프로세스 ID(PID)를 가지며, 부모-자식 관계를 형성합니다. 프로세스는 여러 상태를 가질 수 있으며, 각 상태에 따라 시스템 자원 사용 패턴이 달라집니다.

# 프로세스 상태 코드
# R: Running (실행 중)
# S: Sleeping (대기 중)
# Z: Zombie (종료되었지만 정리되지 않음)
# T: Stopped (일시정지)
# D: Uninterruptible Sleep (디스크 I/O 대기)

프로세스 정보에는 다음과 같은 중요한 정보들이 포함됩니다:

  • PID: Process ID (프로세스 고유 번호)
  • PPID: Parent Process ID (부모 프로세스 번호)
  • %CPU: CPU 사용률
  • %MEM: 메모리 사용률
  • VSZ: Virtual memory Size (가상 메모리 크기)
  • RSS: Resident Set Size (실제 메모리 사용량)
  • TTY: 연결된 터미널
  • STAT: 프로세스 상태
  • START: 시작 시간
  • TIME: 누적 CPU 사용 시간
  • COMMAND: 실행 명령어

프로세스 조회와 모니터링

시스템에서 실행 중인 프로세스를 확인하고 모니터링하는 것은 시스템 관리의 기본입니다.

# 기본 프로세스 조회
ps                              # 현재 터미널의 프로세스만
ps aux                          # 모든 사용자의 모든 프로세스 (BSD 스타일)
ps -ef                          # 모든 프로세스 전체 형식 (System V 스타일)
ps -u username                  # 특정 사용자의 프로세스만

# 프로세스 트리 (계층 구조)
pstree                          # 프로세스 계층 구조를 트리로 표시
pstree -p                       # PID와 함께 표시
pstree username                 # 특정 사용자의 프로세스 트리

# 특정 프로세스 찾기
pgrep python                    # python 관련 프로세스의 PID들
pgrep -f "analysis.py"          # 명령줄에 analysis.py가 포함된 프로세스
pidof python                    # python 프로세스의 모든 PID
ps aux | grep python            # python 관련 프로세스 상세 정보

실시간 모니터링

장시간 실행되는 분석 작업의 진행 상황과 시스템 부하를 실시간으로 모니터링하는 것이 중요합니다.

# top - 실시간 프로세스 모니터링
top                             # 기본 top 실행
top -u username                 # 특정 사용자의 프로세스만
top -p 1234,5678               # 특정 PID들만 모니터링

# top 내에서 사용할 수 있는 키
# q: 종료
# k: 프로세스 종료 (PID 입력 후 시그널 선택)
# M: 메모리 사용량순 정렬
# P: CPU 사용량순 정렬
# T: 실행 시간순 정렬
# u: 특정 사용자 필터링

# htop (향상된 top, 설치 필요)
htop                           # 컬러풀하고 대화형 인터페이스

# watch - 명령어 반복 실행
watch -n 5 "ps aux | grep python"      # 5초마다 python 프로세스 확인
watch "df -h"                           # 디스크 사용량 실시간 모니터링

프로세스 제어

프로세스를 제어하는 것은 시스템 관리와 문제 해결의 핵심입니다. 시그널을 통해 프로세스와 통신할 수 있습니다.

# 시그널을 통한 프로세스 제어
kill PID                        # TERM 시그널 (정상 종료 요청)
kill -9 PID                     # KILL 시그널 (강제 종료)
kill -15 PID                    # TERM 시그널 (명시적)
kill -STOP PID                  # 프로세스 일시정지
kill -CONT PID                  # 일시정지된 프로세스 재시작
kill -HUP PID                   # 설정 파일 재로드 (많은 서비스에서 사용)

# 프로세스 이름으로 제어
killall python                 # 모든 python 프로세스 종료
killall -9 firefox             # 모든 firefox 프로세스 강제 종료
killall -STOP analysis_script   # 분석 스크립트 일시정지

# 패턴으로 프로세스 제어
pkill -f "analysis.py"         # analysis.py를 실행하는 프로세스 종료
pkill -u username              # 특정 사용자의 모든 프로세스 종료
pkill -9 -f "long_running"     # long_running이 포함된 프로세스 강제 종료

주요 시그널들:

시그널번호의미기본 동작
TERM15정상 종료 요청프로세스가 정리 작업 후 종료
KILL9강제 종료즉시 종료 (정리 작업 없음)
STOP19일시정지프로세스 중단
CONT18재시작중단된 프로세스 재개
HUP1재시작/재로드설정 파일 재로드

백그라운드 작업 관리

생물정보학 분석은 종종 몇 시간에서 며칠이 걸리므로 백그라운드 실행과 관리가 필수적입니다.

# 백그라운드 실행
command &                       # 명령어를 백그라운드에서 실행
python analysis.py &            # Python 스크립트 백그라운드 실행
nohup python analysis.py &      # 터미널 종료 후에도 계속 실행

# 작업 제어 (Job Control)
jobs                           # 현재 쉘의 백그라운드 작업 목록
jobs -l                        # 작업 목록을 PID와 함께 표시

fg %1                          # 작업 1을 포그라운드로 가져오기
bg %2                          # 작업 2를 백그라운드에서 재시작
kill %1                        # 작업 1 종료

# 키보드 단축키
# Ctrl+C: 현재 프로세스에 SIGINT 전송 (중단)
# Ctrl+Z: 현재 프로세스에 SIGTSTP 전송 (일시정지)
# Ctrl+\: 현재 프로세스에 SIGQUIT 전송 (강제 종료)

nohup과 장시간 실행

연구 분석 작업은 SSH 연결이 끊어져도 계속 실행되어야 하는 경우가 많습니다.

# nohup 사용법
nohup python analysis.py &                      # 기본 사용법
nohup python analysis.py > output.log 2>&1 &   # 출력을 로그 파일로 저장
nohup python analysis.py < input.txt > output.log 2>&1 &  # 입력과 출력 모두 리다이렉션

# 실행 상태 확인
ps aux | grep analysis.py                       # 프로세스 확인
cat nohup.out                                   # 기본 출력 파일 확인
tail -f output.log                              # 실시간 로그 모니터링

screen과 tmux

터미널 세션을 유지하여 장시간 작업을 안전하게 관리할 수 있는 도구들입니다.

# screen 기본 사용법
screen                                  # 새 screen 세션 시작
screen -S analysis                      # 이름을 지정한 세션 시작
screen -ls                             # 활성 세션 목록 확인
screen -r analysis                     # 세션에 재연결

# screen 내부 명령어 (Ctrl+A가 명령 프리픽스)
# Ctrl+A, d: 세션에서 분리 (detach)
# Ctrl+A, c: 새 창 생성
# Ctrl+A, n: 다음 창으로 이동
# Ctrl+A, p: 이전 창으로 이동
# Ctrl+A, ": 창 목록 보기

# tmux 기본 사용법 (더 현대적)
tmux                                   # 새 tmux 세션 시작
tmux new -s analysis                   # 이름을 지정한 세션
tmux ls                               # 세션 목록
tmux attach -t analysis               # 세션에 재연결
tmux kill-session -t analysis         # 세션 종료

# tmux 내부 명령어 (Ctrl+B가 기본 프리픽스)
# Ctrl+B, d: 세션에서 분리
# Ctrl+B, c: 새 창 생성
# Ctrl+B, %: 세로 분할
# Ctrl+B, ": 가로 분할

시스템 리소스 모니터링

분석 작업이 시스템에 미치는 영향을 모니터링하고 최적화하는 것이 중요합니다.

# 메모리 사용량
free -h                               # 메모리 사용량 (사람이 읽기 쉬운 형태)
free -m                               # MB 단위로 표시
cat /proc/meminfo                     # 상세한 메모리 정보

# CPU 정보
cat /proc/cpuinfo                     # CPU 상세 정보
nproc                                 # CPU 코어 수
lscpu                                 # CPU 아키텍처 정보

# 시스템 부하
uptime                                # 시스템 가동 시간과 로드 평균
w                                     # 로그인한 사용자와 그들의 작업
vmstat 5                              # 5초 간격으로 시스템 통계
iostat 5                              # I/O 통계 (sysstat 패키지 필요)

# 프로세스별 리소스 사용량
ps aux --sort=-%cpu | head -10        # CPU 사용률 상위 10개 프로세스
ps aux --sort=-%mem | head -10        # 메모리 사용률 상위 10개 프로세스

Practice Section: 생물정보학 분석 작업 관리

# 실습 환경 진입
nix develop .#chapter01

이번 실습을 통해 생성되어야 할 최종 outputs 은 없습니다.

실습 1: 기본 프로세스 모니터링

# 현재 시스템 상태 확인
uptime
free -h | grep Mem

# Python 프로세스 확인
pgrep python | wc -l | xargs echo "Python 프로세스 수:"
ps aux | grep python | grep -v grep

# 시스템 부하 확인 (CPU 사용률 상위 5개)
ps aux --sort=-%cpu | head -6

실습 2: 백그라운드 작업 시뮬레이션

# 백그라운드에서 실행
long-task 30 > task_output.log 2>&1 &
task_pid=$!

# 작업 pid 확인
echo $task_pid

# 작업 상태 확인
ps aux | grep long_task | grep -v grep

rm task_output.log

실습 3: nohup을 이용한 안전한 실행

# 연결이 끊어져도 계속 실행되는 작업
nohup long-task 45 > nohup_output.log 2>&1 &
nohup_pid=$!

# PID 저장
echo $nohup_pid > analysis.pid
echo "분석 PID 저장: $(cat analysis.pid)"

# 상태 모니터링
sleep 5
echo "=== 작업 상태 ==="
if kill -0 $nohup_pid 2>/dev/null; then
    echo "작업 실행 중..."
    tail -n 5 nohup_output.log
else
    echo "작업 완료됨"
fi

rm nohup_output.log analysis.pid

실습 4: 프로세스 제어 연습

# 테스트용 무한 루프 백그라운드 실행
# 카운터가 무한히 실행되어 화면에 출력되지만 계속 진행하면 됩니다
infinite-task
test_pid=$!
echo "테스트 프로세스 PID: $test_pid"

# 잠시 실행 후 정상 종료
sleep 10
echo "정상 종료 시그널 전송..."
kill $test_pid

# 정리 확인
sleep 2
if kill -0 $test_pid 2>/dev/null; then
    echo "강제 종료 필요"
    kill -9 $test_pid
else
    echo "정상 종료됨"
fi

실습 5: 시스템 모니터링 스크립트

# 모니터링 스크립트
# 5회 모니터링 실행
for i in {1..5}; do
    monitor
    sleep 3
done

핵심 정리

프로세스 관리 필수 명령어

# 프로세스 확인
ps aux | grep process_name              # 특정 프로세스 찾기
pgrep -f pattern                        # 패턴으로 PID 찾기
top -u username                         # 사용자별 프로세스 모니터링

# 프로세스 제어
kill PID                                # 정상 종료
kill -9 PID                            # 강제 종료
killall process_name                    # 이름으로 종료

# 백그라운드 실행
nohup command > logfile 2>&1 &         # 안전한 백그라운드 실행

장시간 작업 관리 패턴

# 작업 시작
nohup python analysis.py > analysis.log 2>&1 &
echo $! > analysis.pid

# 상태 확인
tail -f analysis.log
ps aux | grep analysis.py

# 안전한 종료
kill $(cat analysis.pid)

다음: 1.4 Text Processing Tools

1.4 Text Processing Tools

핵심 개념

텍스트 처리는 생물정보학의 핵심입니다. FASTA, FASTQ, CSV, GFF 등 대부분의 생물학적 데이터가 텍스트 형태로 저장되며, 이를 효율적으로 처리하고 분석하는 것이 연구의 기본이 됩니다. Unix/Linux의 텍스트 처리 도구들은 강력하고 빠르며, 메모리 효율적입니다.

grep - 패턴 검색의 핵심 도구

grep은 텍스트에서 특정 패턴을 찾는 가장 기본적이고 강력한 도구입니다. 생물정보학에서는 서열 ID 찾기, 특정 조건의 데이터 필터링, 로그 파일 분석 등에 광범위하게 사용됩니다.

# 기본 패턴 검색
grep "pattern" filename             # 패턴이 포함된 줄 찾기
grep -i "pattern" filename          # 대소문자 무시 (case-insensitive)
grep -v "pattern" filename          # 패턴이 없는 줄 (inverse match)
grep -n "pattern" filename          # 줄 번호와 함께 출력
grep -c "pattern" filename          # 매치된 줄 수만 출력

# 여러 파일에서 검색
grep "pattern" *.txt                # 모든 .txt 파일에서 검색
grep -r "pattern" directory/        # 디렉토리 내 모든 파일에서 재귀 검색
grep -l "pattern" *.fasta           # 패턴을 포함한 파일명만 출력
grep -H "pattern" *.csv             # 파일명과 함께 출력

# 정규표현식 사용
grep -E "pattern1|pattern2" file    # OR 조건 (egrep와 동일)
grep "^>" file.fasta                # 줄 시작이 >인 것 (FASTA 헤더)
grep "ATCG$" file                   # 줄 끝이 ATCG인 것
grep "[0-9]\{3,\}" file             # 3자리 이상 숫자
grep "^[ATCG]\{50,\}$" file         # 50bp 이상의 DNA 서열

# 문맥과 함께 검색
grep -A 3 "pattern" file            # 매치된 줄 뒤 3줄 포함
grep -B 2 "pattern" file            # 매치된 줄 앞 2줄 포함
grep -C 5 "pattern" file            # 매치된 줄 앞뒤 5줄 포함

sed - 스트림 에디터

sed는 텍스트를 수정, 변환, 삭제하는 스트림 에디터입니다. 대용량 파일을 메모리에 전부 로드하지 않고도 처리할 수 있어 생물정보학 데이터 처리에 매우 유용합니다.

# 기본 치환 작업
sed 's/old/new/' filename           # 각 줄의 첫 번째 매치만 치환
sed 's/old/new/g' filename          # 모든 매치 치환 (global)
sed 's/old/new/2' filename          # 각 줄의 두 번째 매치만 치환

# 파일 직접 수정
sed -i 's/old/new/g' filename       # 원본 파일을 직접 수정
sed -i.bak 's/old/new/g' filename   # 백업(.bak) 생성 후 수정

# 특정 줄에서만 작업
sed '5s/old/new/' filename          # 5번째 줄에서만 치환
sed '1,10s/old/new/g' filename      # 1-10번째 줄에서만 치환
sed '/pattern/s/old/new/g' filename # 패턴이 있는 줄에서만 치환

# 줄 삭제와 추가
sed '1d' filename                    # 첫 번째 줄 삭제
sed '/pattern/d' filename            # 패턴을 포함한 줄 삭제
sed '1,5d' filename                  # 1-5번째 줄 삭제
sed '5a\새로운 줄' filename          # 5번째 줄 뒤에 줄 추가
sed '/pattern/i\앞에 추가' filename  # 패턴 줄 앞에 추가

# 복잡한 변환
sed -e 's/A/T/g' -e 's/C/G/g' file        # 다중 치환 (DNA 상보서열 일부)
sed 's/\([ATCG]\)\([ATCG]\)/\2\1/g' file  # 백레퍼런스 사용

awk - 패턴 스캐닝과 데이터 추출

awk는 구조화된 텍스트 데이터를 처리하는 강력한 프로그래밍 언어입니다. CSV, TSV 파일이나 고정 폭 데이터를 처리하는 데 특히 유용합니다.

# 기본 구조: awk 'pattern {action}' filename
awk '{print $1}' filename           # 첫 번째 필드(컬럼) 출력
awk '{print $NF}' filename          # 마지막 필드 출력
awk '{print NR, $0}' filename       # 줄 번호와 함께 전체 줄 출력

# 필드 구분자 지정
awk -F',' '{print $1, $3}' file.csv # CSV 파일의 1, 3번째 컬럼
awk -F'\t' '{print $2}' file.tsv    # TSV 파일의 2번째 컬럼
awk -F'|' '{print $2}' fasta.txt    # | 구분자 사용

# 조건문과 패턴 매칭
awk '$3 > 100 {print $1, $3}' file  # 3번째 필드가 100 초과인 줄
awk 'NR > 1 {print}' file           # 첫 번째 줄(헤더) 제외
awk '/^>/ {print}' file.fasta       # FASTA 헤더만 출력
awk 'length($0) > 50' file          # 50자 이상인 줄만

# 수치 계산
awk '{sum += $1} END {print sum}' file          # 첫 번째 컬럼 합계
awk '{print $1, $2 * $3}' file                  # 2, 3번째 컬럼 곱셈
awk 'NR > 1 {count++} END {print count}' file   # 데이터 줄 수 (헤더 제외)
awk '{print length($0)}' file                   # 각 줄의 길이

# 변수와 조건부 처리
awk 'BEGIN {count=0} /^>/ {count++} END {print "Sequences:", count}' file.fasta
awk '{count[$1]++} END {for(i in count) print i, count[i]}' file  # 첫 번째 필드 빈도수

sort와 uniq - 데이터 정렬과 중복 처리

대용량 생물학적 데이터에서 정렬과 중복 제거는 매우 중요한 작업입니다. 이 도구들은 메모리 효율적으로 대용량 파일을 처리할 수 있습니다.

# 기본 정렬
sort filename                      # 사전순 정렬
sort -r filename                   # 역순 정렬
sort -n filename                   # 숫자 순 정렬
sort -nr filename                  # 숫자 역순 정렬

# 필드 기준 정렬
sort -k2 filename                  # 2번째 필드 기준 정렬
sort -k2,2 -k3,3n filename         # 2번째 필드 문자순, 3번째 필드 숫자순
sort -t',' -k3nr file.csv          # CSV에서 3번째 컬럼 숫자 역순

# 고급 정렬 옵션
sort -u filename                   # 정렬 후 중복 제거
sort -S 1G filename                # 메모리 사용량 지정 (대용량 파일용)
sort --parallel=4 filename         # 병렬 처리

# uniq - 중복 처리 (정렬된 파일에서만 작동)
sort filename | uniq               # 중복 제거
sort filename | uniq -c            # 중복 횟수와 함께
sort filename | uniq -d            # 중복된 것만 출력
sort filename | uniq -u            # 유일한 것만 출력

cut, paste, join - 컬럼 조작 도구

구조화된 데이터에서 특정 컬럼을 추출하거나 결합하는 작업은 데이터 분석의 기본입니다.

# cut - 컬럼 추출
cut -d',' -f1,3 file.csv           # CSV 1, 3번째 컬럼
cut -d'\t' -f2- file.tsv           # TSV 2번째 컬럼부터 끝까지
cut -c1-10 filename                # 1-10번째 문자
cut -c5- filename                  # 5번째 문자부터 끝까지
cut -d' ' -f1 --complement file    # 첫 번째 필드를 제외한 모든 필드

# paste - 파일 가로 병합
paste file1.txt file2.txt          # 탭으로 구분하여 병합
paste -d',' file1.txt file2.txt    # 콤마로 구분하여 병합
paste -s file.txt                  # 세로를 가로로 변환

# join - 공통 키로 결합 (정렬된 파일 필요)
join file1.txt file2.txt           # 첫 번째 필드가 키
join -1 2 -2 1 file1.txt file2.txt # file1의 2번째, file2의 1번째 필드가 키
join -t',' file1.csv file2.csv     # CSV 파일 결합

tr - 문자 변환과 삭제

문자 단위의 변환과 정리 작업에 매우 유용한 도구입니다.

# 문자 변환
tr 'a-z' 'A-Z' < file              # 소문자를 대문자로
tr 'ATCG' 'TAGC' < dna.txt         # DNA 상보 서열 변환
tr ' ' '_' < file                  # 공백을 언더스코어로
tr '[:lower:]' '[:upper:]' < file  # 소문자를 대문자로 (POSIX 방식)

# 문자 삭제
tr -d ' ' < file                   # 모든 공백 제거
tr -d '\r' < file                  # 캐리지 리턴 제거 (Windows → Unix)
tr -d '\n' < file                  # 줄바꿈 제거
tr -d '[:digit:]' < file           # 모든 숫자 제거

# 문자 압축
tr -s ' ' < file                   # 연속된 공백을 하나로 압축
tr -s '\n' < file                  # 연속된 빈 줄을 하나로

고급 텍스트 처리 패턴

실제 생물정보학 연구에서 자주 사용되는 복합적인 텍스트 처리 패턴들입니다.

# FASTA 파일 처리 패턴
grep -c "^>" sequences.fasta                    # 서열 개수 세기
grep "^>" sequences.fasta | cut -d'|' -f2       # 서열 ID 추출
awk '/^>/ {print $0} !/^>/ {seq=seq$0} END {print length(seq)}' file.fasta  # 총 서열 길이

# CSV 데이터 분석 패턴
awk -F',' 'NR>1 {sum+=$3; count++} END {print sum/count}' data.csv  # 3번째 컬럼 평균
sort -t',' -k2,2 -k3,3nr data.csv              # 2번째 컬럼별, 3번째 컬럼 내림차순
cut -d',' -f1,3 data.csv | grep -v "^$"        # 1,3번째 컬럼, 빈 줄 제외

# 로그 파일 분석 패턴
grep "ERROR" logfile.txt | wc -l                # 에러 개수
awk '{print $1}' access.log | sort | uniq -c | sort -nr  # IP별 접속 빈도
sed -n '100,200p' large_file.txt               # 100-200번째 줄만 출력

Practice Section: 생물정보학 데이터 처리

# 실습 환경 진입
nix develop .#chapter01

이번 실습을 통해 생성되어야 할 최종 outputs 은 다음과 같습니다.

[drwxr-xr-x]  .
└── [drwxr-xr-x]  bioproject
    ├── [-rw-r--r--]  analysis.log
    ├── [lrwxr-xr-x]  current_data -> data/raw/sequences.fasta
    ├── [lrwxr-xr-x]  current_metadata -> data/raw/metadata.csv
    ├── [drwxr-x---]  data
    │   ├── [drwxr-xr-x]  backup
    │   │   ├── [-rw-------]  metadata.csv
    │   │   └── [-rw-------]  sequences.fasta
    │   ├── [drwxr-xr-x]  processed
    │   │   └── [-rw-r--r--]  sequences_working.fasta
    │   └── [drwx------]  raw
    │       ├── [-rw-r--r--]  lengths.txt
    │       ├── [-rw-r--r--]  metadata.csv
    │       └── [-rw-r--r--]  sequences.fasta
    ├── [-rw-r--r--]  mixed_ids.txt
    └── [drwxr-xr-x]  results
        ├── [-rw-r--r--]  lengths.report
        └── [-rw-r--r--]  report.txt

7 directories, 12 files

실습 1: FASTA 파일 기본 분석

# ./playground/practice/chapter01/bioproject/data/raw
cd ./bioproject/data/raw

# FASTA 파일 기본 정보 추출
echo "서열 개수: $(grep -c '^>' sequences.fasta)"
echo "총 라인 수: $(wc -l < sequences.fasta)"

# 서열 ID와 길이 분석 임시 파일 생성
awk '/^>/ {if(seq) print header, length(seq); header=$0; seq=""}
     !/^>/ {seq=seq$0}
     END {print header, length(seq)}' sequences.fasta > lengths.txt

echo "최단 서열: $(awk '{print $2, $1}' lengths.txt | sort -n | head -1 | awk '{print $2, $1}')bp" > ../../results/lengths.report
echo "최장 서열: $(awk '{print $2, $1}' lengths.txt | sort -n | tail -1 | awk '{print $2, $1}')bp" >> ../../results/lengths.report
echo "평균 길이: $(awk '{sum+=$2} END {print int(sum/NR)}' lengths.txt)bp" >> ../../results/lengths.report

# 보고서 확인
cd ../../results
cat lengths.report

실습 2: CSV 데이터 처리

cd ../data/raw/

# 메타데이터 분석
echo "총 샘플 수: $(tail -n +2 metadata.csv | wc -l)"

# 유기체별 분포
tail -n +2 metadata.csv | cut -d',' -f2 | sort | uniq -c

# 조건별 분석
tail -n +2 metadata.csv | cut -d',' -f3 | sort | uniq -c

# 특정 조건 필터링
awk -F',' '$3=="control" {print $1, $2}' metadata.csv

실습 3: 로그 파일 생성 및 분석

# bioproject 로 이동
cd ../..

# 샘플 로그 파일 생성
cat > analysis.log << 'EOF'
2024-01-15 09:00:00 [INFO] Analysis started
2024-01-15 09:05:12 [INFO] Loading sequences: 150 files
2024-01-15 09:15:30 [WARN] Low quality detected in sample_023
2024-01-15 09:25:45 [ERROR] Failed to process sample_087
2024-01-15 09:30:00 [INFO] Quality control completed
2024-01-15 09:45:22 [INFO] BLAST search started
2024-01-15 10:15:33 [WARN] Timeout in sample_045
2024-01-15 10:30:00 [INFO] Analysis completed
EOF

# 로그 분석
echo "=== 로그 파일 분석 ==="
echo "총 로그 라인: $(wc -l < analysis.log)"
echo "에러 발생: $(grep -c ERROR analysis.log)건"
echo "경고 발생: $(grep -c WARN analysis.log)건"

# 시간대별 로그 개수
echo -e "\n시간대별 로그:"
awk '{print substr($2, 1, 5)}' analysis.log | sort | uniq -c

# 에러와 경고 상세 내용
echo -e "\n문제 상황:"
grep -E "(ERROR|WARN)" analysis.log | cut -d']' -f2-

실습 4: 텍스트 변환과 정리

# 다양한 형식의 서열 ID 정리
cat > mixed_ids.txt << 'EOF'
>gi|123456|sp|P12345|GENE1_ECOLI
>ref|NM_000001.1|
>gb|ABC12345.1|gene_description
>seq_001_sample_A
>SEQ_002_SAMPLE_B
EOF

echo "=== ID 정리 작업 ==="
echo "원본 ID들:"
cat mixed_ids.txt

# ID 정리 (파이프 구분자 제거, 대소문자 통일)
echo -e "\n정리된 ID들:"
grep '^>' mixed_ids.txt | \
    sed 's/^>//' | \
    tr '|' '_' | \
    tr '[:upper:]' '[:lower:]'

# 서열 데이터 정리 (N 제거, 대문자 변환)
echo -e "\nATCGNNATCG" | tr -d 'N' | tr '[:lower:]' '[:upper:]'

실습 5: 복합 데이터 처리 파이프라인

cd ./data/raw
# 복잡한 데이터 처리 예제
echo "=== 복합 데이터 처리 ==="

# FASTA 헤더에서 종 정보 추출 및 통계
grep '^>' sequences.fasta | \
    cut -d'_' -f2 | \
    sort | uniq -c | \
    sort -nr | \
    awk '{printf "%-15s: %d개\n", $2, $1}'

# 메타데이터와 결합하여 분석
echo -e "\n조건별 유기체 분포:"
tail -n +2 metadata.csv | \
    awk -F',' '{print $2, $3}' | \
    sort | uniq -c | \
    awk '{printf "%-10s %-10s: %d개\n", $2, $3, $1}'

# 파일 크기별 정리
echo -e "\n파일 크기 분석:"
ls -la *.{fasta,csv} 2>/dev/null | \
    grep -v '^d' | \
    awk '{print $5, $9}' | \
    sort -nr | \
    awk '{
        size = $1
        if(size > 1000) printf "%-20s: %.1fKB\n", $2, size/1000
        else printf "%-20s: %dB\n", $2, size
    }'

핵심 정리

생물정보학 텍스트 처리 필수 패턴

# FASTA 파일 처리
awk '/^>/ {if(seq) print length(seq); seq=""} !/^>/ {seq=seq$0} END{print length(seq)}' file.fasta
grep -c "^>" file.fasta                     # 서열 개수
grep "^>" file.fasta | cut -d'|' -f2        # ID 추출

# CSV 데이터 분석
awk -F',' 'NR==1 || $3>threshold' data.csv  # 헤더 + 조건 필터
tail -n +2 data.csv | cut -d',' -f2 | sort | uniq -c  # 빈도 분석

# 로그 분석
grep -E "(ERROR|WARN)" logfile | cut -d' ' -f3-  # 에러/경고 메시지만
awk '{print $1}' logfile | sort | uniq -c        # 날짜별 집계

효율적인 대용량 파일 처리

# 메모리 효율적 처리
head -1000 large_file.txt | grep pattern    # 샘플링 후 테스트
sort -S 2G large_file.txt                   # 메모리 할당량 조정
split -l 10000 large_file.txt chunk_        # 파일 분할 후 처리

# 파이프라인 최적화
grep pattern file | head -100               # 조기 종료로 속도 향상
sort file | uniq -c | sort -nr | head -10   # Top 10 빈도 항목

다음: 1.5 Shell Scripting Basics

1.5 Shell Scripting Basics

핵심 개념

Shell 스크립트는 명령어들을 파일에 저장하여 반복 작업을 자동화하는 도구입니다. 생물정보학 연구에서는 대량의 서열 파일 처리, 분석 파이프라인 실행, 시스템 모니터링 등에 필수적입니다.

스크립트 구조와 실행

스크립트의 첫 줄인 shebang(#!/bin/bash)은 어떤 해석기로 실행할지 지정합니다. 이후 변수 설정, 함수 정의, 메인 로직 순으로 구성합니다.

#!/bin/bash
# 스크립트 설명: FASTA 파일 일괄 처리
# 작성자: 연구자명, 날짜: 2024-01-15

# 전역 변수 설정
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
readonly LOG_FILE="$SCRIPT_DIR/processing.log"
readonly DATA_DIR="${1:-data/raw}"

# 로깅 함수
log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 메인 실행 함수
main() {
    log_message "스크립트 시작"
    process_files "$DATA_DIR"
    log_message "스크립트 완료"
}

# 스크립트 실행
main "$@"

실행 권한 부여와 실행 방법:

chmod +x script.sh          # 실행 권한 부여
./script.sh                 # 현재 디렉토리에서 실행
bash script.sh              # bash로 직접 실행
bash -x script.sh           # 디버그 모드 (각 명령어 출력)
bash -n script.sh           # 문법 검사만 수행

변수와 매개변수 처리

변수는 데이터 저장과 스크립트 설정에 사용됩니다. Bash에서는 타입이 없으므로 모든 값이 문자열로 처리되지만, 산술 연산 시 숫자로 해석됩니다.

# 변수 정의 (공백 없이)
project_name="bioanalysis"
data_directory="/home/user/bioproject/data"
max_threads=8
readonly CONFIG_FILE="/etc/analysis.conf"  # 변경 불가능한 상수

# 변수 사용 (중괄호로 명확히 구분)
echo "프로젝트: $project_name"
echo "데이터 위치: ${data_directory}/raw"
echo "출력 파일: ${project_name}_results.txt"

# 환경 변수 설정 (하위 프로세스에서도 사용 가능)
export PYTHONPATH="$HOME/scripts:$PYTHONPATH"
export BLAST_DB_PATH="/usr/local/db/blast"

매개변수는 스크립트 실행 시 전달되는 인자들로, 스크립트의 유연성을 높입니다:

# 특수 변수들
echo "스크립트 이름: $0"           # script.sh
echo "첫 번째 인자: $1"            # 사용자가 전달한 첫 번째 값
echo "두 번째 인자: $2"            # 사용자가 전달한 두 번째 값
echo "모든 인자: $*"               # 모든 인자를 하나의 문자열로
echo "모든 인자: $@"               # 각 인자를 개별 문자열로
echo "인자 개수: $#"               # 전달받은 인자의 수
echo "이전 명령 종료 코드: $?"      # 0=성공, 1-255=오류
echo "현재 프로세스 PID: $$"       # 스크립트의 프로세스 ID

# 기본값 설정 (매개변수가 없을 때 사용)
input_file="${1:-sequences.fasta}"     # $1이 없으면 sequences.fasta
output_dir="${2:-./results}"           # $2가 없으면 ./results
threads="${3:-$(nproc)}"               # $3이 없으면 CPU 코어 수

배열은 여러 값을 하나의 변수에 저장할 때 사용합니다:

# 배열 정의
samples=("ctrl_rep1" "ctrl_rep2" "treat_rep1" "treat_rep2")
declare -a organisms=("E.coli" "B.subtilis" "S.aureus")

# 배열 접근
echo "첫 번째 샘플: ${samples[0]}"
echo "모든 샘플: ${samples[@]}"        # 모든 요소
echo "배열 크기: ${#samples[@]}"       # 요소 개수
echo "인덱스 2-3: ${samples[@]:2:2}"   # 부분 배열

# 배열 순회
for sample in "${samples[@]}"; do
    echo "처리 중: $sample"
done

조건문과 논리 제어

조건문은 파일 존재, 데이터 유효성, 에러 처리 등에 핵심적입니다. Bash의 [[ ]][ ]보다 강력하고 안전한 조건 검사를 제공합니다.

# 파일과 디렉토리 검사
if [[ -f "$input_file" ]]; then
    echo "파일 존재: $input_file"
elif [[ -d "$input_file" ]]; then
    echo "디렉토리 존재: $input_file"
else
    echo "파일/디렉토리 없음: $input_file"
    exit 1
fi

# 파일 속성 검사
[[ -f file ]]        # 일반 파일 존재
[[ -d dir ]]         # 디렉토리 존재
[[ -r file ]]        # 읽기 가능
[[ -w file ]]        # 쓰기 가능
[[ -x file ]]        # 실행 가능
[[ -s file ]]        # 비어있지 않음 (크기 > 0)
[[ -e file ]]        # 존재 (파일이든 디렉토리든)

# 문자열 비교
[[ "$var" == "value" ]]     # 정확히 같음
[[ "$var" != "value" ]]     # 다름
[[ "$var" =~ ^[0-9]+$ ]]    # 정규표현식 매치 (숫자만)
[[ -z "$var" ]]             # 빈 문자열 또는 미정의
[[ -n "$var" ]]             # 비어있지 않은 문자열

# 숫자 비교
[[ $num -eq 10 ]]          # 같음 (equal)
[[ $num -ne 10 ]]          # 다름 (not equal)
[[ $num -gt 5 ]]           # 초과 (greater than)
[[ $num -lt 20 ]]          # 미만 (less than)
[[ $num -ge 5 ]]           # 이상 (greater equal)
[[ $num -le 20 ]]          # 이하 (less equal)

# 논리 연산자
[[ -f "$file" && -r "$file" ]]     # AND: 파일이 존재하고 읽기 가능
[[ "$ext" == "fasta" || "$ext" == "fa" ]]  # OR: fasta 또는 fa 확장자
[[ ! -f "$file" ]]                 # NOT: 파일이 존재하지 않음

case문은 여러 조건을 깔끔하게 처리할 때 유용합니다:

# 파일 확장자별 처리
file_extension="${filename##*.}"
case "$file_extension" in
    "fasta"|"fa"|"fas")
        echo "FASTA 서열 파일 처리"
        process_sequences "$filename"
        ;;
    "fastq"|"fq")
        echo "FASTQ 품질 파일 처리"
        process_reads "$filename"
        ;;
    "csv"|"tsv")
        echo "표 형식 데이터 처리"
        process_table_data "$filename"
        ;;
    "log")
        echo "로그 파일 분석"
        analyze_log "$filename"
        ;;
    *)
        echo "지원하지 않는 파일 형식: $file_extension"
        return 1
        ;;
esac

반복문과 데이터 처리

반복문은 대량의 파일 처리나 반복 작업 자동화에 필수적입니다.

# for 루프 - 파일 글롭 패턴
for file in *.fasta; do
    # 파일이 실제로 존재하는지 확인 (glob이 매치되지 않을 때 대비)
    [[ -f "$file" ]] || continue
    echo "서열 분석 중: $file"
    analyze_sequences "$file"
done

# for 루프 - 숫자 범위
for sample_id in {001..100}; do
    input_file="sample_${sample_id}.fastq"
    if [[ -f "$input_file" ]]; then
        echo "샘플 $sample_id 품질 검사 중"
        quality_check "$input_file"
    fi
done

# for 루프 - 배열 순회
organisms=("E.coli" "B.subtilis" "S.aureus")
for organism in "${organisms[@]}"; do
    echo "유기체 분석: $organism"
    run_blast_search "$organism"
done

# C 스타일 for 루프 (수치 계산)
total_files=100
for ((i=1; i<=total_files; i++)); do
    progress=$((i * 100 / total_files))
    echo "진행률: $progress% ($i/$total_files)"
done

while 루프는 조건이 만족되는 동안 계속 실행되며, 파일 읽기나 대기 작업에 유용합니다:

# 파일 라인별 읽기 (가장 안전한 방법)
while IFS= read -r line; do
    echo "처리 중인 라인: $line"
    # IFS=는 앞뒤 공백 보존, -r은 백슬래시 이스케이프 방지
done < "$input_file"

# CSV 파일 처리 (필드 분리)
while IFS=',' read -r sample_id organism condition expression; do
    echo "샘플: $sample_id, 유기체: $organism, 발현량: $expression"
done < data.csv

# 조건부 대기 (프로세스 완료 대기)
job_pid=$!  # 백그라운드 작업의 PID
while kill -0 "$job_pid" 2>/dev/null; do
    echo "분석 작업 진행 중... (PID: $job_pid)"
    sleep 30
done
echo "분석 작업 완료"

# 무한 루프 (시스템 모니터링)
while true; do
    disk_usage=$(df -h /data | awk 'NR==2 {print $5}')
    echo "[$(date)] 디스크 사용률: $disk_usage"
    [[ "${disk_usage%?}" -gt 90 ]] && echo "경고: 디스크 공간 부족!"
    sleep 300  # 5분마다 확인
done

함수와 모듈화

함수는 코드 재사용과 가독성 향상을 위해 중요합니다. 복잡한 스크립트에서는 기능별로 함수를 분리하여 유지보수를 용이하게 합니다.

# 기본 함수 구조
validate_fasta_file() {
    local input_file="$1"          # local로 함수 내 변수 선언
    local min_sequences="$2"

    # 입력 검증
    if [[ ! -f "$input_file" ]]; then
        echo "에러: 파일 존재하지 않음: $input_file" >&2
        return 1
    fi

    # FASTA 형식 검증
    if ! grep -q "^>" "$input_file"; then
        echo "에러: FASTA 형식이 아님: $input_file" >&2
        return 2
    fi

    # 서열 수 확인
    local seq_count=$(grep -c "^>" "$input_file")
    if [[ $seq_count -lt $min_sequences ]]; then
        echo "경고: 서열 수 부족: $seq_count < $min_sequences" >&2
        return 3
    fi

    echo "검증 완료: $seq_count개 서열 발견"
    return 0  # 성공
}

# 함수 호출과 결과 처리
if validate_fasta_file "sequences.fasta" 10; then
    echo "FASTA 파일 검증 성공"
    process_sequences "sequences.fasta"
else
    exit_code=$?
    echo "검증 실패 (코드: $exit_code)"
    exit $exit_code
fi

고급 함수 활용 패턴:

# 옵션 처리 함수
parse_command_line() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -i|--input)
                readonly INPUT_FILE="$2"
                shift 2
                ;;
            -o|--output)
                readonly OUTPUT_DIR="$2"
                shift 2
                ;;
            -t|--threads)
                readonly THREADS="$2"
                shift 2
                ;;
            --dry-run)
                readonly DRY_RUN=true
                shift
                ;;
            -v|--verbose)
                readonly VERBOSE=true
                shift
                ;;
            -h|--help)
                show_usage
                exit 0
                ;;
            --)
                shift
                break
                ;;
            -*)
                echo "에러: 알 수 없는 옵션 $1" >&2
                show_usage
                exit 1
                ;;
            *)
                break
                ;;
        esac
    done

    # 필수 매개변수 검증
    if [[ -z "$INPUT_FILE" ]]; then
        echo "에러: 입력 파일 필수 (-i 옵션)" >&2
        exit 1
    fi
}

# 도움말 함수
show_usage() {
    cat << EOF
사용법: $0 [옵션]

필수 옵션:
    -i, --input FILE     입력 FASTA 파일

선택 옵션:
    -o, --output DIR     출력 디렉토리 (기본값: ./results)
    -t, --threads NUM    병렬 처리 스레드 수 (기본값: $(nproc))
    --dry-run           실제 실행 없이 명령어만 출력
    -v, --verbose       상세 진행 상황 출력
    -h, --help          이 도움말 표시

예시:
    $0 -i sequences.fasta -o analysis_results -t 8 -v
    $0 --input data.fa --output results --threads 4 --dry-run
EOF
}

# 에러 처리 함수
handle_error() {
    local exit_code=$1
    local line_number=$2
    local command="$3"

    echo "에러 발생!" >&2
    echo "  파일: $0" >&2
    echo "  라인: $line_number" >&2
    echo "  명령어: $command" >&2
    echo "  종료 코드: $exit_code" >&2

    # 정리 작업
    cleanup_temp_files
    exit $exit_code
}

# 에러 트랩 설정
trap 'handle_error $? $LINENO "$BASH_COMMAND"' ERR

입출력 제어와 로깅

입출력 리다이렉션은 스크립트의 결과를 파일로 저장하거나 다른 프로그램과 연결할 때 사용됩니다.

# 기본 리다이렉션
command > output.txt           # 표준 출력을 파일로 (덮어쓰기)
command >> output.txt          # 표준 출력을 파일에 추가
command 2> error.txt           # 표준 에러를 파일로
command > output.txt 2>&1      # 출력과 에러를 같은 파일로
command &> all_output.txt      # 위와 동일 (bash 4.0+)
command > /dev/null 2>&1       # 모든 출력 숨기기

# Here Document (설정 파일 생성 등)
cat << EOF > blast_config.txt
# BLAST 검색 설정
database: nr
evalue: 1e-5
max_targets: 100
output_format: 6
EOF

# Here String (간단한 입력)
grep "pattern" <<< "$variable_content"

파이프라인과 고급 처리:

# 복잡한 데이터 처리 파이프라인
grep "^>" sequences.fasta | \
    sed 's/^>//' | \
    cut -d'|' -f2 | \
    sort | \
    uniq -c | \
    sort -nr > organism_counts.txt

# 조건부 파이프라인
if [[ $verbose == true ]]; then
    analysis_command | tee analysis.log
else
    analysis_command > analysis.log 2>&1
fi

# 명명된 파이프 (FIFO) 활용
mkfifo analysis_pipe
analysis_producer > analysis_pipe &
analysis_consumer < analysis_pipe
rm analysis_pipe

로깅 시스템 구현:

# 로깅 함수들
setup_logging() {
    readonly LOG_DIR="logs"
    readonly LOG_FILE="$LOG_DIR/analysis_$(date +%Y%m%d_%H%M%S).log"

    mkdir -p "$LOG_DIR"
    exec 3>&1 4>&2  # 원본 stdout, stderr 백업
    exec 1> >(tee -a "$LOG_FILE")
    exec 2> >(tee -a "$LOG_FILE" >&2)
}

log_info() {
    echo "[INFO $(date '+%Y-%m-%d %H:%M:%S')] $*"
}

log_warn() {
    echo "[WARN $(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
}

log_error() {
    echo "[ERROR $(date '+%Y-%m-%d %H:%M:%S')] $*" >&2
}

# 사용 예시
setup_logging
log_info "분석 시작"
log_warn "임시 파일 사용량 높음"
log_error "BLAST 데이터베이스 연결 실패"

Practice Section: 생물정보학 연구 자동화

# 실습 환경 진입
nix develop .#chapter01

이번 실습을 통해 생성되어야 할 최종 outputs 은 다음과 같습니다.

[drwxr-xr-x]  .
└── [drwxr-xr-x]  bioproject
    ├── [-rw-r--r--]  analysis.log
    ├── [lrwxr-xr-x]  current_data -> data/raw/sequences.fasta
    ├── [lrwxr-xr-x]  current_metadata -> data/raw/metadata.csv
    ├── [drwxr-x---]  data
    │   ├── [drwxr-xr-x]  backup
    │   │   ├── [-rw-------]  metadata.csv
    │   │   └── [-rw-------]  sequences.fasta
    │   ├── [drwxr-xr-x]  processed
    │   │   └── [-rw-r--r--]  sequences_working.fasta
    │   └── [drwx------]  raw
    │       ├── [-rw-r--r--]  lengths.txt
    │       ├── [-rw-r--r--]  metadata.csv
    │       └── [-rw-r--r--]  sequences.fasta
    ├── [-rw-r--r--]  mixed_ids.txt
    ├── [drwxr-xr-x]  results
    │   ├── [-rw-r--r--]  lengths.report
    │   └── [-rw-r--r--]  report.txt
    └── [drwxr-xr-x]  scripts
        ├── [-rwxr-xr-x]  analyze_fasta.sh
        ├── [-rwxr-xr-x]  batch_process.sh
        ├── [-rwxr-xr-x]  conditional_process.sh
        ├── [-rwxr-xr-x]  function_example.sh
        ├── [drwxr-xr-x]  results
        │   └── [-rw-r--r--]  sequences_analysis.txt
        └── [-rwxr-xr-x]  simple_monitor.sh

9 directories, 18 files

실습 1: 기본 FASTA 분석 스크립트

cd ./bioproject/

mkdir scripts

cat > analyze_fasta.sh << 'EOF'
#!/bin/bash
# FASTA 파일 기본 분석

input_file="$1"

# 입력 검증
if [[ ! -f "$input_file" ]]; then
    echo "사용법: $0 <fasta_file>"
    exit 1
fi

echo "=== FASTA 분석: $(basename "$input_file") ==="

# 서열 개수
seq_count=$(grep -c "^>" "$input_file")
echo "서열 개수: $seq_count"

# 서열 길이 계산
awk '
/^>/ { if(seq) print length(seq); seq="" }
!/^>/ { seq = seq $0 }
END { if(seq) print length(seq) }
' "$input_file" > lengths.txt

# 통계 출력
echo "최소 길이: $(sort -n lengths.txt | head -1)bp"
echo "최대 길이: $(sort -n lengths.txt | tail -1)bp"
echo "평균 길이: $(awk '{sum+=$1} END {print int(sum/NR)}' lengths.txt)bp"

rm lengths.txt
EOF

chmod +x analyze_fasta.sh
./analyze_fasta.sh ../data/raw/sequences.fasta

# 실행 권한은 다음과 같이 삭제할 수 있음
# chmod -x analyze_fasta.sh

실습 2: 다중 파일 처리

cat > batch_process.sh << 'EOF'
#!/bin/bash
# 여러 FASTA 파일 일괄 처리

input_dir="${1:-../data/raw}"
output_dir="${2:-results}"

mkdir -p "$output_dir"

echo "일괄 처리 시작: $(date)"
count=0

for file in "$input_dir"/*.fasta; do
    [[ -f "$file" ]] || continue

    filename=$(basename "$file" .fasta)
    echo "처리 중: $filename"

    ./analyze_fasta.sh "$file" > "$output_dir/${filename}_analysis.txt"
    ((count++))
done

echo "처리 완료: $count 개 파일"
EOF

chmod +x batch_process.sh
./batch_process.sh

실습 3: 조건부 처리

cat > conditional_process.sh << 'EOF'
#!/bin/bash
# 파일 크기에 따른 조건부 처리

for file in ../data/raw/*.fasta; do
    [[ -f "$file" ]] || continue

    size=$(wc -c < "$file")
    filename=$(basename "$file")

    if [[ $size -gt 1000 ]]; then
        echo "대용량 파일: $filename ($size bytes)"
        # 특별한 처리 로직
    else
        echo "소용량 파일: $filename ($size bytes)"
        # 빠른 처리 로직
    fi
done
EOF

chmod +x conditional_process.sh
./conditional_process.sh

실습 4: 간단한 모니터링

CAUTION: macOS 는 free 명령어 없음

cat > simple_monitor.sh << 'EOF'
#!/bin/bash
# 시스템 리소스 간단 모니터링

monitor_system() {
  echo "=== 시스템 상태 $(date '+%H:%M:%S') ==="

  # CPU 사용률
  cpu=$(top -l 1 | grep "Cpu(s)" | awk '{print $2}')
  echo "CPU: $cpu"

  # 메모리 사용률
  memory=$(free | grep Mem | awk '{printf "%.1f%%", $3/$2 * 100.0}')
  echo "메모리: $memory"

  # 실행 중인 분석 프로세스
  python_count=$(pgrep python | wc -l)
  echo "Python 프로세스: $python_count 개"

  echo "---"
}

# 5번 모니터링 (실제로는 while true 사용)
for _ in {1..5}; do
  monitor_system
  sleep 2
done
EOF

chmod +x simple_monitor.sh
./simple_monitor.sh

실습 5: 함수와 매개변수

cat > function_example.sh << 'EOF'
#!/bin/bash
# 함수 활용 예제

# FASTA 파일 검증 함수
validate_fasta() {
  local file="$1"

  if [[ ! -f "$file" ]]; then
    echo "에러: 파일 없음 $file"
    return 1
  fi

  if ! grep -q "^>" "$file"; then
    echo "에러: FASTA 형식 아님 $file"
    return 1
  fi

  echo "검증 성공: $file"
  return 0
}

# 서열 개수 세기 함수
count_sequences() {
  local file="$1"
  local count
  count=$(grep -c "^>" "$file")
  echo "$file: $count 개 서열"
}

# 메인 실행
for file in ../data/raw/*.fasta; do
  if validate_fasta "$file"; then
    count_sequences "$file"
  fi
done
EOF

chmod +x function_example.sh
./function_example.sh

핵심 정리

기본 스크립트 패턴

#!/bin/bash
# 안전한 스크립트 작성
set -euo pipefail                    # 에러 시 종료, 미정의 변수 금지

# 입력 검증
input_file="${1:?사용법: $0 <file>}"
[[ -f "$input_file" ]] || { echo "파일 없음"; exit 1; }

# 함수 활용
process_file() {
    local file="$1"
    echo "처리 중: $(basename "$file")"
    # 작업 수행
}

# 반복 처리
for file in *.fasta; do
    [[ -f "$file" ]] || continue
    process_file "$file"
done

생물정보학 스크립트 요약

# FASTA 검증
[[ $(head -1 "$file") =~ ^> ]] || { echo "FASTA 아님"; exit 1; }

# 서열 개수
seq_count=$(grep -c "^>" "$file")

# 로그와 함께 실행
command 2>&1 | tee logfile.log

# 진행률 표시
echo "진행률: $((current * 100 / total))%"

다음: Chapter 2: Development Environment with Nix

Chapter 2: Development Environment with Nix

핵심 개념

Nix는 함수형 패키지 관리자로, 재현 가능하고 결정론적인 개발 환경을 제공합니다. 생물정보학 연구에서 중요한 의존성 충돌 해결과 환경 일관성 문제를 근본적으로 해결합니다.

소프트웨어 의존성 관리 문제

현대 생물정보학 연구는 수많은 소프트웨어 도구에 의존합니다. Python, R, BLAST, BWA, SAMtools 등 각 도구는 서로 다른 버전의 라이브러리를 요구하며, 이는 복잡한 의존성 충돌을 야기합니다.

# 전형적인 의존성 충돌 시나리오
$ pip install biopython==1.79
$ pip install tensorflow==2.8.0
ERROR: tensorflow requires numpy>=1.20.0,<1.24.0
       but biopython requires numpy>=1.17.0

$ conda install -c bioconda blast
$ conda install -c conda-forge pytorch
ERROR: Package conflicts detected

이러한 문제들의 근본 원인:

  1. 전역 네임스페이스: 시스템 전체에서 하나의 버전만 설치 가능
  2. 가변 상태: 패키지 설치/제거가 기존 환경을 변경
  3. 시점 의존성: 설치 시점에 따라 다른 버전 설치
  4. 불완전한 격리: 가상환경도 시스템 라이브러리에 의존

Nix의 해결 접근법

Nix는 함수형 프로그래밍 원리를 패키지 관리에 적용하여 이러한 문제들을 근본적으로 해결합니다:

불변성(Immutability): 설치된 패키지는 절대 변경되지 않습니다. 각 패키지는 고유한 해시를 가진 경로에 저장되며, 내용이 바뀌면 새로운 경로에 새로 설치됩니다.

/nix/store/abc123...python3-3.8.16/
/nix/store/def456...python3-3.9.18/
/nix/store/ghi789...numpy-1.21.0-python3.8/
/nix/store/jkl012...numpy-1.23.0-python3.9/

순수 함수: 같은 입력(설정)은 항상 같은 출력(환경)을 생성합니다. 시점이나 시스템 상태에 관계없이 동일한 환경이 구성됩니다.

완전한 격리: 각 패키지는 명시적으로 선언된 의존성만 참조할 수 있으며, 시스템의 다른 부분과 완전히 격리됩니다.

Nix 언어와 표현식

Nix는 자체 함수형 언어를 사용하여 패키지와 환경을 선언적으로 정의합니다. 이 언어는 JSON과 유사하지만 변수, 함수, 조건문을 지원합니다.

# 기본 개발 환경 정의
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    python3
    python3Packages.biopython
    python3Packages.numpy
    python3Packages.pandas
  ];

  shellHook = ''
    echo "생물정보학 분석 환경"
    python --version
  '';
}

Nix Store와 패키지 관리

Nix Store는 모든 패키지가 저장되는 불변 저장소입니다. 각 패키지는 입력(소스 코드, 의존성, 빌드 스크립트)의 해시를 기반으로 한 고유한 경로에 저장됩니다.

경로 구조: /nix/store/[32자리 해시]-[패키지명]-[버전]/

결정론적 빌드: 같은 입력으로 빌드하면 항상 같은 결과를 생성합니다. 이는 완전한 재현성을 보장합니다.

참조 투명성: 패키지의 의존성이 명시적으로 기록되어 있어, 실행 시점에 정확히 어떤 버전의 라이브러리를 사용하는지 추적 가능합니다.

개발 워크플로우에서의 장점

프로젝트별 환경: 각 연구 프로젝트가 독립된 환경을 가질 수 있습니다.

# RNA-seq 분석 환경
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  buildInputs = with pkgs; [
    python3
    python3Packages.pandas
    python3Packages.matplotlib
    r-packages.DESeq2
    salmon
    fastp
  ];
}

팀 협업: 환경 설정을 코드와 함께 버전 관리하여 모든 팀원이 동일한 환경을 사용할 수 있습니다.

실험과 롤백: 새로운 도구를 안전하게 실험하고, 문제가 생기면 즉시 이전 환경으로 복원할 수 있습니다.

학습 로드맵

Chapter 2.1: Programming Paradigms

명령형과 함수형 프로그래밍의 차이점을 이해하고, 패키지 관리에서 함수형 접근법의 장점을 학습합니다.

Chapter 2.2: Functional Programming

불변성, 순수 함수, 참조 투명성 등 함수형 프로그래밍의 핵심 개념과 Nix에서의 적용을 다룹니다.

Chapter 2.3: Attribute Sets

Nix의 데이터 구조인 속성집합을 이해하고, 구조화된 설정 관리 방법을 학습합니다.

Chapter 2.4: Nix Basics

Nix 언어의 기본 문법과 패키지 관리 명령어를 실습하며 개발 환경 구성 방법을 익힙니다.

Chapter 2.5: Nix Core Concepts

Derivation, Nix Store, 의존성 추적 등 Nix의 내부 동작 원리를 심화 학습합니다.

사전 요구사항

설치: Nix 패키지 관리자가 시스템에 설치되어 있어야 합니다.

# Linux/macOS 설치
# Windows 의 경우 WSL2 를 설치 후, WSL2 위에 Nix 설치 필요
curl -L https://nixos.org/nix/install | sh
source ~/.nix-profile/etc/profile.d/nix.sh

# 설치 확인
nix --version

배경 지식:

  • 기본적인 패키지 관리 경험 (pip, conda, apt 등)
  • 명령줄 인터페이스 사용법
  • 프로그래밍 언어 사용 경험 (Python, R 등)

다음: 2.1 Programming Paradigms

2.1 Programming Paradigms

핵심 개념

프로그래밍 패러다임은 문제를 해결하는 사고방식입니다. 명령형은 "어떻게(How)" 할지 단계를 지시하고, 함수형은 "무엇을(What)" 원하는지 선언합니다. 패키지 관리에서 이 차이는 안정성과 재현성에 결정적 영향을 미칩니다.

명령형 프로그래밍: "어떻게(How)" 중심

명령형 프로그래밍은 컴퓨터에게 작업을 수행할 구체적인 단계를 순서대로 지시하는 방식입니다. 변수의 상태를 지속적으로 변경하며 원하는 결과에 도달합니다.

핵심 특징:

  • 순차적 실행: 명령어를 위에서 아래로 차례대로 실행
  • 상태 변경: 변수와 메모리 상태를 지속적으로 수정
  • 제어 구조: if, for, while 등으로 실행 흐름 제어
  • 부작용: 함수 실행이 외부 상태에 영향을 미침
# 명령형 방식: 평균 계산 과정
numbers = [245, 189, 567, 123, 445]
total = 0
count = 0

# 단계별로 상태를 변경
for number in numbers:
    total = total + number    # 상태 변경
    count = count + 1         # 상태 변경
    print(f"단계 {count}: 합계={total}")

average = total / count
print(f"최종 평균: {average}")

실행 과정에서 변수들의 상태가 계속 변화합니다:

단계 1: 합계=245
단계 2: 합계=434
단계 3: 합계=1001
단계 4: 합계=1124
단계 5: 합계=1569
최종 평균: 313.8

명령형 패키지 관리의 문제점

전통적인 패키지 관리자들(pip, conda, apt)은 명령형 접근법을 사용합니다. 시스템 상태를 단계적으로 변경하여 원하는 환경을 구성합니다.

# 명령형 패키지 관리 예시
conda create -n analysis python=3.8    # 환경 생성
conda activate analysis                 # 환경 활성화
pip install biopython==1.78            # 패키지 설치 (시스템 상태 변경)
pip install pandas==1.5.0              # 또 다른 패키지 설치 (상태 변경)
pip install tensorflow==2.8.0          # 의존성 충돌 가능성

발생 가능한 문제들:

  1. 시점 의존성: 설치 시점에 따라 다른 버전의 의존성이 설치됨
# 2023년 3월 실행
$ pip install requests
Successfully installed requests-2.28.2 urllib3-1.26.15

# 2023년 8월 실행 (같은 명령어, 다른 결과)
$ pip install requests
Successfully installed requests-2.31.0 urllib3-2.0.4
  1. 상태 오염: 새로운 패키지 설치가 기존 환경을 변경
$ pip install package_a  # numpy 1.21.0 설치됨
$ pip install package_b  # numpy 1.23.0으로 업그레이드됨
# package_a가 의존하던 numpy 1.21.0 사라짐
  1. 불완전한 롤백: 이전 상태로 완전한 복원이 어려움
$ pip uninstall package_b
# numpy는 1.23.0 그대로 남음
# package_a는 1.21.0을 기대하지만 다른 버전 사용

함수형 프로그래밍: "무엇을(What)" 중심

함수형 프로그래밍은 원하는 결과를 함수들의 조합으로 선언적으로 표현하는 방식입니다. 상태 변경 없이 입력을 출력으로 변환하는 순수 함수들을 조합합니다.

핵심 특징:

  • 불변성: 생성된 값은 절대 변경되지 않음
  • 순수 함수: 같은 입력에 항상 같은 출력, 부작용 없음
  • 선언적: 과정보다는 결과를 선언
  • 조합성: 작은 함수들을 조합하여 복잡한 작업 수행
# 함수형 방식: 평균 계산
numbers = [245, 189, 567, 123, 445]

# 중간 상태 변경 없이 직접 계산
average = sum(numbers) / len(numbers)
print(f"평균: {average}")

# 또는 함수 조합으로
from statistics import mean
average = mean(numbers)
print(f"평균: {average}")

함수형 방식에서는 중간 상태 변경 없이 입력에서 출력으로 직접 변환됩니다.

함수형 패키지 관리의 장점

Nix는 함수형 접근법을 패키지 관리에 적용합니다. 환경을 상태 변경이 아닌 함수의 결과로 취급합니다.

# 함수형 패키지 관리: 환경을 선언적으로 정의
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    python38                    # 정확한 버전 지정
    python38Packages.biopython # 호환성 보장된 버전
    python38Packages.pandas    # 자동 의존성 해결
    python38Packages.tensorflow
  ];
}

함수형 접근법의 장점:

  1. 결정론적: 같은 입력(설정)은 항상 같은 출력(환경) 생성
  2. 불변성: 기존 환경을 변경하지 않고 새로운 환경 생성
  3. 안전한 실험: 새 환경 테스트가 기존 환경에 영향 없음
  4. 완전한 롤백: 이전 환경으로 즉시 완전 복원

패러다임 비교: 실제 시나리오

생물정보학 연구에서 자주 발생하는 시나리오를 통해 두 패러다임의 차이를 살펴보겠습니다.

시나리오: RNA-seq 분석을 위해 Python 환경에 여러 패키지를 설치해야 하는 상황

명령형 접근법:

# 단계별 환경 구성 (상태 변경)
conda create -n rnaseq python=3.8
conda activate rnaseq
pip install pandas==1.5.0       # 시스템 상태 변경
pip install numpy==1.21.0       # 의존성 추가
pip install scikit-learn         # numpy 버전 충돌 가능성
pip install matplotlib          # 추가 의존성 설치

# 문제 발생 시
pip uninstall scikit-learn      # 부분적 롤백
# numpy, matplotlib은 변경된 상태로 남음

함수형 접근법:

# 전체 환경을 하나의 함수로 정의
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    python38
    python38Packages.pandas      # 호환성 보장
    python38Packages.numpy       # 정확한 버전 자동 선택
    python38Packages.scikit-learn # 의존성 자동 해결
    python38Packages.matplotlib
  ];
}

재현성 관점에서의 차이

명령형의 재현성 문제:

  • 시간에 따른 패키지 저장소 변화
  • 설치 순서에 따른 결과 차이
  • 시스템 상태에 따른 영향

함수형의 재현성 보장:

  • 입력이 같으면 결과도 항상 동일
  • 시점과 무관한 일관된 환경
  • 완전한 의존성 추적
# 완전한 재현성을 위한 버전 고정
{ pkgs ? import (fetchTarball {
    url = "https://github.com/NixOS/nixpkgs/archive/22.11.tar.gz";
    sha256 = "11w3wn2yjhaa5pv20gbfbirvjq6i3m7pqrq2msf0g7cv44vijwgw";
  }) {}
}:

pkgs.mkShell {
  buildInputs = with pkgs; [
    python38                     # 정확히 Python 3.8.16
    python38Packages.biopython  # 정확히 BioPython 1.79
    python38Packages.pandas     # 정확히 Pandas 1.5.3
  ];
}

Practice Section: 패러다임 차이 체험

실습 1: 명령형 패키지 관리 시뮬레이션

# 전통적인 방식의 문제점 확인
echo "=== 명령형 패키지 관리 시뮬레이션 ==="

# 가상의 패키지 설치 과정 시뮬레이션
echo "1. 기본 Python 환경"
echo "   python: 3.8.0"
echo "   pip 패키지: 없음"

echo -e "\n2. pandas 설치 후"
echo "   python: 3.8.0"
echo "   pandas: 1.5.0"
echo "   numpy: 1.21.0 (pandas 의존성)"

echo -e "\n3. tensorflow 설치 후"
echo "   python: 3.8.0"
echo "   pandas: 1.5.0"
echo "   numpy: 1.23.0 (tensorflow가 요구하는 버전으로 업그레이드)"
echo "   tensorflow: 2.8.0"
echo "   WARNING: pandas가 기대하던 numpy 버전 변경됨"

echo -e "\n4. 문제 발생"
echo "   pandas 일부 기능이 새로운 numpy 버전과 호환성 문제"

실습 2: Nix 함수형 접근 체험

# 함수형 패키지 관리 체험
echo "=== Nix 함수형 접근법 ==="

# 기본 분석 환경 정의
cat > analysis-env.nix << 'EOF'
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    python3
    python3Packages.pandas
    python3Packages.numpy
  ];

  shellHook = ''
    echo "기본 분석 환경"
    echo "Python: $(python --version)"
    python -c "import pandas; print(f'Pandas: {pandas.__version__}')"
  '';
}
EOF

echo "기본 환경 정의 완료"
echo "실행: nix-shell analysis-env.nix"

실습 3: 환경 격리 확인

# 다른 환경 정의
cat > ml-env.nix << 'EOF'
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    python3
    python3Packages.tensorflow
    python3Packages.scikit-learn
    python3Packages.numpy
  ];

  shellHook = ''
    echo "머신러닝 환경"
    echo "TensorFlow와 scikit-learn 포함"
    python -c "
try:
    import tensorflow as tf
    print(f'TensorFlow: {tf.__version__}')
except:
    print('TensorFlow 사용 불가')
"
  '';
}
EOF

echo "머신러닝 환경 정의 완료"
echo "두 환경이 서로 영향을 주지 않음을 확인할 수 있습니다"

실습 4: 재현성 테스트

# 재현 가능한 환경 정의
cat > reproducible-env.nix << 'EOF'
{ pkgs ? import (fetchTarball {
    # 특정 nixpkgs 버전 고정
    url = "https://github.com/NixOS/nixpkgs/archive/nixos-23.11.tar.gz";
    sha256 = "1ndiv385w1qyb3b18vw13991fzb9wg4cl21wglk89grsfsnra41k";
  }) {}
}:

pkgs.mkShell {
  buildInputs = with pkgs; [
    python3                    # 정확한 버전 고정
    python3Packages.biopython
    python3Packages.pandas
  ];

  shellHook = ''
    echo "재현 가능한 환경"
    echo "언제 실행해도 동일한 패키지 버전"
    python --version
  '';
}
EOF

echo "재현 가능한 환경 정의 완료"
echo "이 환경은 1년 후에도 동일한 결과를 보장합니다"

실습 5: 함수 조합 패턴

# 재사용 가능한 환경 구성 요소
cat > composable-env.nix << 'EOF'
{ pkgs ? import <nixpkgs> {} }:

let
  # 기본 Python 환경
  basePython = pkgs.python3;

  # 패키지 그룹들
  dataPackages = ps: with ps; [ pandas numpy matplotlib ];
  bioPackages = ps: with ps; [ biopython ];
  mlPackages = ps: with ps; [ scikit-learn ];

in {
  # 조합으로 다양한 환경 생성
  dataEnv = basePython.withPackages dataPackages;

  bioEnv = basePython.withPackages (ps:
    dataPackages ps ++ bioPackages ps
  );

  fullEnv = basePython.withPackages (ps:
    dataPackages ps ++ bioPackages ps ++ mlPackages ps
  );
}
EOF

echo "조합 가능한 환경 정의 완료"
echo "작은 구성 요소들을 조합하여 다양한 환경 생성"

핵심 정리

패러다임 비교 요약

특성명령형 (전통적)함수형 (Nix)
접근법단계별 명령선언적 정의
상태가변, 변경됨불변, 보존됨
재현성시점 의존적항상 동일
롤백부분적, 복잡완전, 즉시
격리불완전완전
실험위험안전

실무 선택 가이드

# 함수형 패키지 관리가 적합한 경우
# - 재현성이 중요한 연구
# - 여러 프로젝트 동시 진행
# - 팀 협업 환경
# - 장기간 유지보수

# 간단한 환경 정의 예시
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  buildInputs = with pkgs; [
    python3
    python3Packages.requests
  ];
}

다음: 2.2 Functional Programming

2.2 Functional Programming Fundamentals

핵심 개념

함수형 프로그래밍의 핵심 원리인 불변성, 순수 함수, 참조 투명성은 패키지 관리에서 예측 가능하고 재현 가능한 환경을 보장합니다. Nix는 이러한 원리를 통해 의존성 충돌과 환경 불일치 문제를 근본적으로 해결합니다.

불변성 (Immutability)

불변성은 한 번 생성된 데이터를 절대 변경하지 않는 원칙입니다. 전통적인 패키지 관리에서는 시스템 전역 상태가 지속적으로 변경되어 예측 불가능한 결과를 초래하지만, Nix는 불변성을 통해 이 문제를 해결합니다.

# 전통적 방식: 상태 변경으로 인한 문제
pip install numpy==1.21.0        # 시스템 상태 변경
pip install tensorflow==2.8.0     # numpy가 1.23.0으로 업그레이드됨
python analyze.py                 # 기존 코드가 다른 numpy 버전으로 실행됨 - 예측 불가능

Nix의 불변성 구현:

# Nix Store에서의 불변성
# /nix/store/abc123...numpy-1.21.0/  - 절대 변경되지 않음
# /nix/store/def456...numpy-1.23.0/  - 새로운 경로에 설치
# 두 버전이 동시에 존재하며 서로 간섭하지 않음

# 환경 정의에서의 불변성
let pythonVersion = "38";
in {
  # pythonVersion = "39";  # 오류! 재할당 불가능
  environment = {
    python = "python${pythonVersion}";
    packages = ["numpy" "pandas"];  # 리스트도 불변
  };
}

불변성의 실제 활용:

# 환경 버전 관리
let
  version_2023_01 = {
    python = "38";
    numpy = "1.21.0";
    tensorflow = "2.8.0";
  };

  version_2023_06 = {
    python = "38";
    numpy = "1.23.0";
    tensorflow = "2.11.0";
  };
in {
  # 두 환경이 동시에 존재, 서로 간섭 없음
  old_analysis = buildEnv version_2023_01;    # 기존 분석 재현
  new_analysis = buildEnv version_2023_06;    # 새로운 분석
}

순수 함수 (Pure Functions)

순수 함수는 동일한 입력에 대해 항상 동일한 출력을 반환하며, 외부 상태를 변경하지 않습니다. Nix의 모든 빌드 과정은 순수 함수로 설계되어 완전한 재현성을 보장합니다.

# 순수 함수와 비순수 함수 비교
import random
import time

# 비순수 함수: 같은 입력이지만 다른 출력
def impure_analysis(data):
    seed = int(time.time())  # 현재 시간에 의존
    random.seed(seed)
    noise = random.random()   # 매번 다른 값
    return sum(data) + noise

result1 = impure_analysis([1, 2, 3])  # 6.123...
result2 = impure_analysis([1, 2, 3])  # 6.789... (다른 결과)

# 순수 함수: 같은 입력은 항상 같은 출력
def pure_analysis(data, seed=42):
    random.seed(seed)        # 고정된 시드
    noise = random.random()   # 항상 같은 값
    return sum(data) + noise

result1 = pure_analysis([1, 2, 3])  # 6.6394...
result2 = pure_analysis([1, 2, 3])  # 6.6394... (같은 결과)

Nix에서의 순수 함수 구현:

# 환경 빌드 함수 - 순수 함수
buildPythonEnv = { python, packages, version ? "stable" }:
  python.withPackages (ps:
    map (pkg: ps.${pkg}) packages
  );

# 같은 입력은 항상 같은 출력
env1 = buildPythonEnv {
  python = pkgs.python38;
  packages = ["numpy" "pandas"];
};

env2 = buildPythonEnv {
  python = pkgs.python38;
  packages = ["numpy" "pandas"];
};

# env1과 env2는 완전히 동일한 환경
# 시점, 시스템 상태와 무관하게 항상 같은 결과

의존성 해결의 순수성:

# 의존성 해결 함수 - 수학적으로 결정론적
resolveDependencies = packages:
  let
    # 모든 의존성을 수집하고 버전 호환성 계산
    allDeps = flattenDependencies packages;
    compatible = filterCompatible allDeps;
    resolved = solveVersionConstraints compatible;
  in resolved;

# 같은 패키지 리스트는 항상 같은 의존성 해결 결과
tensorflow_deps = resolveDependencies ["tensorflow" "numpy" "pandas"];
# 언제 실행해도 같은 버전 조합이 선택됨

참조 투명성 (Referential Transparency)

참조 투명성은 표현식을 그 결과값으로 교체해도 프로그램의 동작이 변하지 않는 성질입니다. Nix에서는 모든 패키지 참조가 참조 투명성을 만족합니다.

# 참조 투명성 예제
let
  pythonVersion = "38";
  pythonPackage = pkgs."python${pythonVersion}";
  numpyPackage = pkgs.python38Packages.numpy;
in {
  # 다음 표현들은 모두 동등하며 교체 가능
  environment1 = {
    buildInputs = [ pythonPackage numpyPackage ];
  };

  environment2 = {
    buildInputs = [ pkgs.python38 pkgs.python38Packages.numpy ];
  };

  environment3 = {
    buildInputs = [ pkgs."python38" pkgs.python38Packages."numpy" ];
  };

  # 모든 environment는 완전히 동일
}

표현식 치환의 안전성:

# 함수 정의
mkAnalysisEnv = python: ps: python.withPackages ps;

# 사용 방법 1: 함수 호출
env1 = mkAnalysisEnv pkgs.python38 (ps: [ps.numpy ps.pandas]);

# 사용 방법 2: 함수를 인라인으로 치환
env2 = pkgs.python38.withPackages (ps: [ps.numpy ps.pandas]);

# env1과 env2는 완전히 동일 - 참조 투명성

의존성 관리에서의 함수형 원리 적용

함수형 원리들이 결합되어 복잡한 의존성 문제를 해결하는 방식을 살펴보겠습니다.

# 복합 환경 구성에서의 함수형 접근
let
  # 기본 도구들 (불변)
  baseTools = ["git" "curl" "wget"];

  # 언어별 패키지 정의 (순수 함수)
  pythonPackages = ps: with ps; [numpy pandas matplotlib];
  rPackages = rps: with rps; [ggplot2 dplyr tidyr];

  # 환경 조합 함수 (참조 투명)
  combineEnvs = python: r: basePackages:
    let
      pythonEnv = python.withPackages pythonPackages;
      rEnv = r.withPackages rPackages;
    in basePackages ++ [pythonEnv rEnv];

in {
  # 다양한 환경 조합 - 모두 예측 가능하고 재현 가능
  dataScience = combineEnvs pkgs.python38 pkgs.R baseTools;
  machineLearning = combineEnvs pkgs.python39 pkgs.R (baseTools ++ ["docker"]);

  # 환경 간 격리 보장 - 서로 간섭하지 않음
  oldProject = combineEnvs pkgs.python37 pkgs.R baseTools;
  newProject = combineEnvs pkgs.python310 pkgs.R baseTools;
}

함수형 vs 명령형 비교

생물정보학 연구 환경에서의 실제 차이점을 구체적으로 살펴보겠습니다.

명령형 방식의 문제점:

# 시간에 따른 환경 변화 (명령형)
# 2023년 1월
conda create -n bioproject python=3.8
conda activate bioproject
conda install biopython=1.79 numpy=1.21.0

# 2023년 6월 - 패키지 추가
conda install tensorflow=2.8.0
# numpy가 자동으로 1.23.0으로 업그레이드됨

# 2023년 12월 - 문제 발생
python old_analysis.py
# 에러: numpy 1.23.0과 호환되지 않는 코드

함수형 방식의 해결:

# 환경 정의 (함수형) - 시점 무관 재현성
{ pkgs ? import <nixpkgs> {} }:

let
  # 2023년 1월 환경 정의
  bioproject_v1 = pkgs.python38.withPackages (ps: with ps; [
    biopython   # 1.79
    numpy       # 1.21.0 (자동 호환성 해결)
  ]);

  # 2023년 6월 환경 정의
  bioproject_v2 = pkgs.python38.withPackages (ps: with ps; [
    biopython   # 1.79
    numpy       # 1.21.0 (기존 호환성 유지)
    tensorflow  # 2.8.0
  ]);

in {
  # 두 환경이 동시에 존재
  old_analysis = pkgs.mkShell { buildInputs = [ bioproject_v1 ]; };
  new_analysis = pkgs.mkShell { buildInputs = [ bioproject_v2 ]; };
}

Practice Section: 함수형 원리 실습

실습 1: 불변성 확인

cd ~/bioproject/practice

# Python으로 불변성 개념 확인
cat > immutability_demo.py << 'EOF'
# 가변 객체의 문제
data = [1, 2, 3]
original = data
data.append(4)
print(f"Original: {original}")  # [1, 2, 3, 4] - 의도치 않은 변경

# 불변 접근법
data = [1, 2, 3]
new_data = data + [4]
print(f"Original: {data}")      # [1, 2, 3] - 원본 보존
print(f"New: {new_data}")       # [1, 2, 3, 4] - 새 데이터
EOF

python immutability_demo.py

실습 2: 순수 함수 비교

# 순수 함수와 비순수 함수 비교
cat > pure_function_demo.py << 'EOF'
import random

# 비순수 함수
def impure_analyze(sequences):
    random.seed()  # 매번 다른 시드
    noise = random.random()
    return len(sequences) + noise

# 순수 함수
def pure_analyze(sequences, seed=42):
    random.seed(seed)  # 고정 시드
    noise = random.random()
    return len(sequences) + noise

sequences = ["ATCG", "GCTA", "TTAA"]
print("비순수:", impure_analyze(sequences), impure_analyze(sequences))
print("순수:", pure_analyze(sequences), pure_analyze(sequences))
EOF

python pure_function_demo.py

실습 3: Nix 환경 불변성

# Nix 환경 불변성 테스트
cat > env_immutable.nix << 'EOF'
{pkgs ? import <nixpkgs> {}}:
let
  baseEnv = ["git" "curl"];
  pythonEnv = pkgs.python38.withPackages (ps: [ps.numpy]);
in {
  environment = baseEnv ++ [pythonEnv];
  # baseEnv는 변경되지 않음 - 불변성
}
EOF

nix eval -f env_immutable.nix --json

실습 4: 환경 조합

# 함수형 환경 조합
cat > compose_env.nix << 'EOF'
{pkgs ? import <nixpkgs> {}}:
let
  makeEnv = python: extraPkgs:
    python.withPackages (ps: with ps; [numpy] ++ extraPkgs);

  dataEnv = makeEnv pkgs.python38 (ps: [ps.pandas]);
  mlEnv = makeEnv pkgs.python38 (ps: [ps.scikit-learn]);
in {
  data = dataEnv;
  ml = mlEnv;
}
EOF

nix eval -f compose_env.nix

실습 5: 참조 투명성 확인

# 참조 투명성 테스트
cat > reference_transparency.nix << 'EOF'
{pkgs ? import <nixpkgs> {}}:
let
  pythonVer = "38";
  # 다음 두 표현은 완전히 동일
  env1 = pkgs."python${pythonVer}";
  env2 = pkgs.python38;
in {
  same = env1 == env2;  # true - 참조 투명성
  env1_path = env1;
  env2_path = env2;
}
EOF

nix eval -f reference_transparency.nix

핵심 정리

함수형 원리 요약

# 불변성: 한 번 정의된 값은 변경 불가
let value = "immutable";
in { inherit value; }

# 순수 함수: 같은 입력 → 같은 출력
buildEnv = packages: python.withPackages (ps: packages);

# 참조 투명성: 표현식을 값으로 치환 가능
let pkg = pkgs.python38;
in { env1 = pkg; env2 = pkgs.python38; }  # 동일

실용적 패턴

# 환경 격리 패턴
nix-shell env1.nix      # 프로젝트 1
nix-shell env2.nix      # 프로젝트 2 (완전 격리)

# 재현성 보장 패턴
nix-shell --pure env.nix    # 외부 환경 차단

다음: 2.3 Attribute Sets and Configuration

2.3 Attribute Sets and Configuration Management

핵심 개념

속성집합(Attribute Sets)은 Nix의 핵심 데이터 구조로, 이름-값 쌍으로 구성된 구조화된 설정을 표현합니다. JSON과 달리 변수, 함수, 조건문을 지원하여 복잡한 환경 설정을 재사용 가능하고 유지보수 가능하게 관리할 수 있습니다.

속성집합 기본 구조

속성집합은 중괄호로 둘러싸인 키-값 쌍의 집합입니다. 각 키는 문자열이며, 값은 모든 Nix 타입이 될 수 있습니다.

# 기본 속성집합 구문
{
  name = "bioanalysis";
  version = "1.0.0";
  language = "python";
  dependencies = ["numpy" "pandas" "biopython"];

  # 중첩 속성집합
  config = {
    threads = 8;
    memory = "16GB";
    gpu = false;
  };

  # 동적 속성명
  "python-version" = "3.8";
  "blast-db-path" = "/usr/local/db/blast";
}

속성 접근과 조작:

let
  project = {
    name = "rnaseq-analysis";
    samples = ["ctrl1" "ctrl2" "treat1" "treat2"];
    pipeline = {
      quality_control = true;
      alignment = "star";
      quantification = "salmon";
    };
  };
in {
  # 점 표기법으로 접근
  project_name = project.name;                    # "rnaseq-analysis"
  aligner = project.pipeline.alignment;           # "star"

  # 동적 접근
  first_sample = builtins.elemAt project.samples 0;  # "ctrl1"

  # 속성 존재 확인
  has_gpu = project ? gpu;                        # false (속성 없음)
  has_name = project ? name;                      # true
}

속성집합 연산과 조합

속성집합은 다양한 연산을 통해 조합하고 변형할 수 있습니다. 이는 설정 상속과 확장에 매우 유용합니다.

let
  baseConfig = {
    python = "3.8";
    memory = "8GB";
    timeout = 3600;
    packages = ["numpy" "pandas"];
  };

  # 속성집합 병합 (오른쪽이 우선)
  mlConfig = baseConfig // {
    memory = "16GB";              # 기존 값 오버라이드
    gpu = true;                   # 새 속성 추가
    packages = baseConfig.packages ++ ["tensorflow" "pytorch"];  # 리스트 확장
  };

  # 조건부 속성 추가
  productionConfig = baseConfig // (
    if useGPU then { gpu = true; gpu_memory = "8GB"; } else {}
  );

  # 속성 제거
  lightConfig = removeAttrs baseConfig ["timeout"];

in {
  inherit baseConfig mlConfig productionConfig lightConfig;
}

고급 속성집합 조작:

let
  # 속성 이름 동적 생성
  mkSampleConfig = sampleId: condition: {
    "sample_${sampleId}" = {
      id = sampleId;
      condition = condition;
      replicate = if condition == "control" then 1 else 2;
    };
  };

  # 여러 속성집합 병합
  allSamples =
    mkSampleConfig "001" "control" //
    mkSampleConfig "002" "treatment" //
    mkSampleConfig "003" "control";

  # 속성 필터링과 변환
  controlSamples = builtins.listToAttrs (
    builtins.filter (item: item.value.condition == "control")
      (builtins.map (name: {
        name = name;
        value = allSamples.${name};
      }) (builtins.attrNames allSamples))
  );

in {
  inherit allSamples controlSamples;
}

JSON vs Nix 속성집합 비교

정적 JSON 설정의 한계와 Nix 속성집합의 장점을 구체적으로 살펴보겠습니다.

JSON 설정의 제약사항:

{
  "projects": {
    "rnaseq_project": {
      "python": "3.8",
      "packages": ["pandas", "numpy", "deseq2"],
      "memory": "16GB",
      "samples": 24
    },
    "chipseq_project": {
      "python": "3.8",
      "packages": ["pandas", "numpy", "macs2"],
      "memory": "32GB",
      "samples": 12
    },
    "methylation_project": {
      "python": "3.9",
      "packages": ["pandas", "numpy", "methylpy"],
      "memory": "64GB",
      "samples": 48
    }
  }
}

문제점들:

  • Python 3.8, 공통 패키지 중복 정의
  • 총 메모리 요구량, 샘플 수 계산 불가능
  • 조건부 로직 (GPU 사용 여부) 구현 어려움
  • 설정 템플릿 재사용 불가능

Nix 속성집합 해결책:

let
  # 공통 설정 추출
  commonConfig = {
    basePackages = ["pandas" "numpy"];
    defaultPython = "3.8";
    standardMemory = "16GB";
  };

  # 프로젝트 생성 함수
  mkProject = { python ? commonConfig.defaultPython
              , extraPackages ? []
              , memoryMultiplier ? 1
              , samples
              , useGPU ? false
              }: {
    inherit python samples;
    packages = commonConfig.basePackages ++ extraPackages;
    memory = "${toString (16 * memoryMultiplier)}GB";
    gpu = useGPU;
    estimated_runtime = samples * 2;  # 동적 계산
  };

in {
  projects = {
    rnaseq = mkProject {
      extraPackages = ["deseq2"];
      samples = 24;
    };

    chipseq = mkProject {
      extraPackages = ["macs2"];
      memoryMultiplier = 2;
      samples = 12;
    };

    methylation = mkProject {
      python = "3.9";
      extraPackages = ["methylpy"];
      memoryMultiplier = 4;
      samples = 48;
      useGPU = true;
    };
  };

  # 자동 계산된 통계
  totalSamples = builtins.foldl' (acc: proj: acc + proj.samples) 0
    (builtins.attrValues projects);
  totalMemory = builtins.foldl' (acc: proj: acc + (builtins.fromJSON (builtins.substring 0 2 proj.memory))) 0
    (builtins.attrValues projects);
}

환경별 설정 관리

실제 연구 환경에서는 개발, 테스트, 프로덕션 환경에 따라 다른 설정이 필요합니다. Nix 속성집합은 이를 체계적으로 관리할 수 있습니다.

let
  # 기본 환경 설정
  baseEnvironment = {
    python = "3.8";
    packages = ["numpy" "pandas" "matplotlib"];
    threads = 4;
    logging = "info";
  };

  # 환경별 오버라이드
  environments = {
    development = baseEnvironment // {
      packages = baseEnvironment.packages ++ ["ipython" "jupyter"];
      logging = "debug";
      debug_mode = true;
    };

    testing = baseEnvironment // {
      packages = baseEnvironment.packages ++ ["pytest" "coverage"];
      threads = 2;
      timeout = 300;
    };

    production = baseEnvironment // {
      threads = 16;
      memory_limit = "64GB";
      logging = "warning";
      optimization = true;
      backup_enabled = true;
    };

    cluster = baseEnvironment // {
      threads = 32;
      memory_limit = "256GB";
      distributed = true;
      scheduler = "slurm";
      queue = "gpu";
    };
  };

  # 환경 선택 함수
  selectEnvironment = envName:
    if environments ? ${envName}
    then environments.${envName}
    else builtins.throw "Unknown environment: ${envName}";

in {
  inherit environments;

  # 현재 환경 (환경 변수나 매개변수로 제어)
  current = selectEnvironment "development";

  # 환경 비교
  envComparison = builtins.mapAttrs (name: env: {
    inherit (env) threads;
    package_count = builtins.length env.packages;
    has_debug = env ? debug_mode;
  }) environments;
}

복잡한 패키지 의존성 관리

생물정보학 도구들은 복잡한 의존성 관계를 가집니다. 속성집합을 통해 이를 체계적으로 관리할 수 있습니다.

let
  # 도구별 요구사항 정의
  tools = {
    blast = {
      version = "2.12.0";
      dependencies = ["ncbi-blast+"];
      memory_per_thread = "2GB";
      database_required = true;
    };

    bwa = {
      version = "0.7.17";
      dependencies = ["bwa"];
      memory_per_thread = "1GB";
      index_required = true;
    };

    star = {
      version = "2.7.10";
      dependencies = ["star"];
      memory_per_thread = "8GB";
      index_required = true;
    };

    salmon = {
      version = "1.9.0";
      dependencies = ["salmon"];
      memory_per_thread = "4GB";
      index_required = true;
    };
  };

  # 분석 파이프라인 정의
  pipelines = {
    dna_alignment = {
      tools = ["bwa" "samtools"];
      estimated_memory = 32;
      parallel_jobs = 4;
    };

    rna_quantification = {
      tools = ["star" "salmon"];
      estimated_memory = 64;
      parallel_jobs = 2;
    };

    sequence_search = {
      tools = ["blast"];
      estimated_memory = 16;
      parallel_jobs = 8;
    };
  };

  # 파이프라인별 리소스 계산
  calculateResources = pipelineName:
    let
      pipeline = pipelines.${pipelineName};
      toolConfigs = map (toolName: tools.${toolName}) pipeline.tools;
      maxMemoryPerThread = builtins.foldl' builtins.max 0
        (map (tool: builtins.fromJSON (builtins.substring 0 1 tool.memory_per_thread)) toolConfigs);
    in {
      total_memory = pipeline.estimated_memory;
      memory_per_job = maxMemoryPerThread * pipeline.parallel_jobs;
      dependencies = builtins.concatLists (map (tool: tool.dependencies) toolConfigs);
    };

in {
  inherit tools pipelines;

  # 각 파이프라인의 리소스 요구사항
  resource_requirements = builtins.mapAttrs (name: _: calculateResources name) pipelines;
}

Practice Section: 속성집합 실습

실습 1: 기본 속성집합 조작

# basic_attrs.nix
let
  project = {
    name = "genomics";
    samples = 10;
    organism = "E.coli";
  };
in {
  # 속성 접근
  project_info = "${project.name}: ${project.organism}";
  sample_count = project.samples;

  # 속성 확장
  extended = project // { version = "1.0"; };
}
nix eval -f basic_attrs.nix --json

실습 2: 설정 병합

# config_merge.nix
let
  base = { python = "3.8"; memory = "8GB"; };
  gpu_config = { gpu = true; memory = "16GB"; };
in {
  cpu_env = base;
  gpu_env = base // gpu_config;

  # 메모리 비교
  memory_diff = gpu_config.memory != base.memory;
}
nix eval -f config_merge.nix

실습 3: 동적 속성 생성

# dynamic_attrs.nix
let
  samples = ["ctrl" "treat"];
  mkSample = name: { "${name}_config" = { id = name; processed = false; }; };
in
  builtins.foldl' (acc: sample: acc // mkSample sample) {} samples
nix eval -f dynamic_attrs.nix --json

실습 4: 조건부 설정

# conditional_config.nix
let
  useGPU = true;
  baseConfig = { threads = 4; memory = "8GB"; };
in
  baseConfig // (if useGPU then {
    gpu = true;
    gpu_memory = "8GB";
    threads = 8;
  } else {})
nix eval -f conditional_config.nix

실습 5: 환경 설정 함수

# env_function.nix
let
  mkEnv = {name, python ? "3.8", packages ? []}:
    {
      environment_name = name;
      python_version = python;
      package_list = ["numpy"] ++ packages;
      package_count = builtins.length (["numpy"] ++ packages);
    };
in {
  basic = mkEnv { name = "basic"; };
  advanced = mkEnv {
    name = "advanced";
    python = "3.9";
    packages = ["tensorflow" "pytorch"];
  };
}
nix eval -f env_function.nix --json

핵심 정리

속성집합 필수 연산

# 기본 조작
attrs = { a = 1; b = 2; };
extended = attrs // { c = 3; };      # 병합
value = attrs.a;                     # 접근
exists = attrs ? c;                  # 존재 확인

# 고급 조작
filtered = removeAttrs attrs ["a"];   # 속성 제거
names = builtins.attrNames attrs;     # 키 목록
values = builtins.attrValues attrs;   # 값 목록

실용적 패턴

# 환경 설정 패턴
baseEnv // (if condition then extraConfig else {})

# 다중 병합 패턴
config1 // config2 // config3

# 동적 속성 패턴
{ "${name}_${version}" = value; }

다음: 2.4 Nix Language Basics

2.4 Nix Language Basics

핵심 개념

Nix 언어는 함수형 도메인 특화 언어(DSL)로, 패키지와 환경을 선언적으로 정의하기 위해 설계되었습니다. 표현식 기반 언어로서 모든 구문이 값을 생성하며, 강력한 타입 추론과 지연 평가를 통해 복잡한 시스템 구성을 간결하게 표현할 수 있습니다.

기본 데이터 타입

Nix는 동적 타입 언어이지만 강한 타입 시스템을 가지고 있습니다. 런타임에 타입 검사가 수행되며, 타입 불일치 시 명확한 오류 메시지를 제공합니다.

# 기본 타입들
let
  # 정수와 부동소수점
  sample_count = 42;
  expression_level = 3.14159;

  # 불린 값
  use_gpu = true;
  debug_mode = false;

  # null 값 (옵션 타입 표현)
  optional_config = null;

  # 문자열 (다양한 정의 방법)
  simple_string = "ATCGTTAA";
  interpolated = "Sample count: ${toString sample_count}";

  # 다중 줄 문자열 (들여쓰기 자동 제거)
  fasta_sequence = ''
    >seq1_sample_001
    ATCGATCGATCGATCGATCG
    GCTAGCTAGCTAGCTAGCTA
  '';

  # 경로 타입 (문자열과 구별됨)
  script_path = ./analysis.py;
  data_directory = /home/user/bioproject/data;

in {
  # 타입 확인
  sample_type = builtins.typeOf sample_count;        # "int"
  string_type = builtins.typeOf simple_string;       # "string"
  path_type = builtins.typeOf script_path;           # "path"
}

문자열 조작과 변환:

let
  sequence = "ATCGATCG";
  sample_id = "sample_001";
  count = 42;

in {
  # 문자열 연결
  filename = "${sample_id}_sequences.fasta";
  full_path = "/data/" + sample_id + "/results.txt";

  # 숫자-문자열 변환
  count_string = toString count;                     # "42"
  parsed_number = builtins.fromJSON "123";          # 123

  # 문자열 길이와 부분 문자열
  seq_length = builtins.stringLength sequence;      # 8
  first_codon = builtins.substring 0 3 sequence;    # "ATC"

  # 문자열 분할과 결합
  codons = builtins.filter (s: s != "")
    (builtins.split "(.{3})" sequence);             # ["ATC" "GAT" "CG"]
}

리스트 조작

리스트는 Nix에서 가장 중요한 컬렉션 타입입니다. 불변이며 다양한 타입의 요소를 포함할 수 있습니다.

let
  # 리스트 정의 (공백으로 구분)
  samples = ["ctrl_rep1" "ctrl_rep2" "treat_rep1" "treat_rep2"];
  mixed_list = [1 "hello" true ./path];

  # 리스트 연산
  basic_packages = ["numpy" "pandas"];
  ml_packages = ["tensorflow" "pytorch"];
  all_packages = basic_packages ++ ml_packages;

in {
  # 리스트 접근
  first_sample = builtins.elemAt samples 0;         # "ctrl_rep1"
  sample_count = builtins.length samples;           # 4

  # 리스트 조작
  reversed = builtins.genList (i: builtins.elemAt samples (3 - i)) 4;
  indexed = builtins.genList (i: {
    index = i;
    sample = builtins.elemAt samples i;
  }) (builtins.length samples);

  # 리스트 필터링과 변환
  control_samples = builtins.filter
    (s: builtins.match "ctrl.*" s != null) samples;

  uppercase_samples = map
    (s: builtins.replaceStrings ["ctrl" "treat"] ["CTRL" "TREAT"] s) samples;

  # 리스트 폴딩 (reduce)
  total_length = builtins.foldl'
    (acc: str: acc + builtins.stringLength str) 0 samples;
}

고급 리스트 조작:

let
  sequences = [
    { id = "seq1"; length = 150; gc_content = 0.45; }
    { id = "seq2"; length = 200; gc_content = 0.52; }
    { id = "seq3"; length = 100; gc_content = 0.38; }
  ];

in {
  # 조건부 필터링
  long_sequences = builtins.filter (seq: seq.length > 140) sequences;
  high_gc = builtins.filter (seq: seq.gc_content > 0.5) sequences;

  # 변환과 집계
  sequence_ids = map (seq: seq.id) sequences;
  total_bases = builtins.foldl' (acc: seq: acc + seq.length) 0 sequences;
  average_gc = (builtins.foldl' (acc: seq: acc + seq.gc_content) 0.0 sequences)
              / (builtins.length sequences);

  # 그룹화 (고급 패턴)
  by_length_category = builtins.groupBy
    (seq: if seq.length < 150 then "short" else "long") sequences;
}

함수 정의와 호출

Nix의 함수는 일급 객체이며, 커링을 자동으로 지원합니다. 모든 함수는 단일 인자를 받아 단일 값을 반환합니다.

let
  # 단순 함수
  double = x: x * 2;

  # 다중 인자 함수 (커링)
  add = x: y: x + y;
  multiply = x: y: z: x * y * z;

  # 부분 적용
  add10 = add 10;
  double_and_add5 = x: add 5 (double x);

  # 속성집합 패턴 매칭
  mkSample = { id, condition, replicate ? 1 }:
    {
      sample_name = "${condition}_${id}_rep${toString replicate}";
      inherit id condition replicate;
    };

  # 가변 인자 함수 (... 패턴)
  mkAnalysisConfig = { packages, threads ? 4, memory ? "8GB", ... }@args:
    {
      inherit packages threads memory;
      extra_config = removeAttrs args ["packages" "threads" "memory"];
    };

in {
  # 함수 호출
  result1 = double 21;                             # 42
  result2 = add 15 27;                             # 42
  result3 = add10 32;                              # 42

  # 속성집합 패턴 매칭 사용
  sample1 = mkSample { id = "001"; condition = "control"; };
  sample2 = mkSample { id = "002"; condition = "treatment"; replicate = 3; };

  # 가변 인자 사용
  config = mkAnalysisConfig {
    packages = ["numpy" "pandas"];
    threads = 8;
    debug = true;
    timeout = 3600;
  };
}

고차 함수와 함수 조합:

let
  # 고차 함수 예제
  applyToAll = f: list: map f list;
  compose = f: g: x: f (g x);

  # 조건부 함수 적용
  conditional = pred: f: x: if pred x then f x else x;

  # 함수 리스트 적용
  pipeline = functions: input:
    builtins.foldl' (acc: f: f acc) input functions;

in {
  # 고차 함수 사용
  doubled_numbers = applyToAll double [1 2 3 4 5];

  # 함수 조합
  add_then_double = compose double (add 5);
  result = add_then_double 10;                     # (10 + 5) * 2 = 30

  # 파이프라인 처리
  process_sequence = pipeline [
    (s: builtins.replaceStrings ["T"] ["U"] s)     # DNA to RNA
    (s: builtins.stringLength s)                   # 길이 계산
    (l: l / 3)                                     # 코돈 수
  ];

  codon_count = process_sequence "ATCGATCGTAG";    # 11 / 3 = 3
}

let-in과 with 표현식

let-in은 지역 변수를 정의하고, with는 속성집합의 스코프를 확장합니다. 이들은 코드의 가독성과 재사용성을 크게 향상시킵니다.

# let-in 표현식의 구조
let
  # 변수와 함수 정의 (상호 재귀 가능)
  python_version = "3.8";
  packages = ["numpy" "pandas" "matplotlib"];

  # 함수 정의
  mkPythonEnv = version: pkgs:
    "python${version} with packages: ${toString pkgs}";

  # 상호 재귀 정의
  fibonacci = n: if n <= 1 then n else fibonacci (n-1) + fibonacci (n-2);

  # 중첩 let 표현식
  analysis_config = let
    base_memory = 8;
    thread_multiplier = 2;
  in {
    memory = "${toString (base_memory * thread_multiplier)}GB";
    threads = 4 * thread_multiplier;
  };

in {
  # let에서 정의한 변수들 사용
  environment = mkPythonEnv python_version packages;
  fib_10 = fibonacci 10;
  inherit analysis_config;
}

with 표현식의 활용:

let
  project_config = {
    name = "bioanalysis";
    version = "2.1.0";
    organism = "E.coli";
    samples = 24;
    author = "researcher";
  };

  system_config = {
    threads = 8;
    memory = "32GB";
    storage = "1TB";
    gpu = true;
  };

in {
  # with를 사용한 스코프 확장
  project_summary = with project_config;
    "Project ${name} v${version} studying ${organism} with ${toString samples} samples by ${author}";

  # 다중 with 사용
  resource_summary = with project_config; with system_config;
    "${name} requires ${toString threads} threads and ${memory} memory for ${toString samples} samples";

  # with의 우선순위 (오른쪽이 우선)
  mixed_scope = with project_config; with system_config; {
    # system_config의 name이 있다면 그것이 우선
    inherit name version threads memory;
  };

  # inherit with 패턴
  extracted_config = with project_config; {
    inherit name version organism;  # project_config에서 가져옴
    analysis_date = "2024-01-15";   # 새로 정의
  };
}

조건문과 제어 구조

Nix의 조건문은 표현식이므로 항상 값을 반환합니다. 이는 함수형 언어의 특징으로, 부작용 없는 조건부 로직을 구성할 수 있습니다.

let
  # 기본 조건문
  sample_count = 50;
  use_gpu = true;
  organism = "E.coli";

  # 단순 조건문
  analysis_mode = if sample_count > 100 then "batch" else "interactive";

  # 중첩 조건문
  memory_requirement =
    if sample_count < 10 then "4GB"
    else if sample_count < 50 then "16GB"
    else if sample_count < 200 then "32GB"
    else "64GB";

  # 조건부 속성집합
  gpu_config = if use_gpu then {
    gpu_memory = "8GB";
    cuda_version = "11.8";
    gpu_threads = 1024;
  } else {};

  # 조건부 리스트 구성
  packages = ["numpy" "pandas"] ++
    (if use_gpu then ["cupy" "tensorflow-gpu"] else ["tensorflow-cpu"]) ++
    (if organism == "human" then ["pysam" "pybedtools"] else []);

in {
  # 고급 조건부 로직
  environment_config = {
    inherit analysis_mode memory_requirement packages;

    # 조건부 병합
    system = { threads = 4; } // gpu_config;

    # 조건부 함수 적용
    optimizations = if sample_count > 1000
      then ["parallel" "vectorized" "cached"]
      else ["standard"];
  };

  # assert를 통한 조건 검증
  validated_config =
    assert sample_count > 0;
    assert builtins.elem organism ["E.coli" "human" "mouse"];
    environment_config;
}

재귀와 리스트 처리

Nix에서 반복은 재귀를 통해 구현됩니다. 내장 함수들이 대부분의 일반적인 패턴을 제공하지만, 커스텀 재귀 함수가 필요한 경우도 있습니다.

let
  # 재귀 함수 예제
  factorial = n: if n <= 1 then 1 else n * factorial (n - 1);

  # 리스트 재귀 처리
  sum_list = list:
    if list == [] then 0
    else builtins.head list + sum_list (builtins.tail list);

  # 더 안전한 리스트 처리
  safe_sum = list:
    builtins.foldl' (acc: x: acc + x) 0 list;

  # 트리 구조 처리 (재귀적 속성집합)
  process_tree = tree:
    if builtins.isAttrs tree
    then builtins.mapAttrs (name: subtree: process_tree subtree) tree
    else if builtins.isList tree
    then map process_tree tree
    else tree;  # 기본값

  # 조건부 재귀
  find_in_list = predicate: list:
    if list == [] then null
    else if predicate (builtins.head list) then builtins.head list
    else find_in_list predicate (builtins.tail list);

in {
  # 재귀 함수 사용
  fact_10 = factorial 10;
  list_sum = sum_list [1 2 3 4 5];
  safe_result = safe_sum [10 20 30];

  # 실제 데이터 처리 예제
  sample_data = [
    { id = "S001"; quality = 95; length = 150; }
    { id = "S002"; quality = 87; length = 200; }
    { id = "S003"; quality = 92; length = 175; }
  ];

  high_quality = find_in_list (s: s.quality > 90) sample_data;

  # 중첩 데이터 처리
  experiment = {
    samples = sample_data;
    analysis = {
      quality_threshold = 90;
      length_filter = 160;
    };
  };

  processed_experiment = process_tree experiment;
}

Practice Section: Nix 언어 실습

실습 1: 기본 타입과 변수

# basic_types.nix
let
  sample_id = "ctrl_001";
  read_count = 1500000;
  quality_score = 28.5;
  is_paired = true;
in {
  # 문자열 조작
  filename = "${sample_id}_processed.fastq";

  # 숫자 계산
  million_reads = read_count / 1000000.0;

  # 타입 확인
  types = {
    id_type = builtins.typeOf sample_id;
    count_type = builtins.typeOf read_count;
    score_type = builtins.typeOf quality_score;
  };
}
nix eval -f basic_types.nix --json

실습 2: 리스트와 함수

# list_functions.nix
let
  samples = ["ctrl1" "ctrl2" "treat1" "treat2"];

  # 함수 정의
  add_suffix = suffix: name: "${name}_${suffix}";
  filter_control = builtins.filter (s: builtins.match "ctrl.*" s != null);

in {
  # 리스트 변환
  fastq_files = map (add_suffix "R1.fastq") samples;
  control_samples = filter_control samples;
  sample_count = builtins.length samples;
}
nix eval -f list_functions.nix

실습 3: 조건문과 패턴 매칭

# conditionals.nix
let
  sample_size = 150;
  organism = "human";

  memory_calc = size:
    if size < 50 then "4GB"
    else if size < 200 then "16GB"
    else "32GB";

in {
  required_memory = memory_calc sample_size;

  packages = ["numpy"] ++
    (if organism == "human" then ["pysam"] else []) ++
    (if sample_size > 100 then ["dask"] else []);

  analysis_type = if sample_size > 200 then "cluster" else "local";
}
nix eval -f conditionals.nix

실습 4: let-in과 with 표현식

# let_with.nix
let
  project = {
    name = "rnaseq";
    samples = 24;
    organism = "mouse";
  };

  config = let
    base_mem = 8;
    threads = 4;
  in {
    memory = "${toString (base_mem * 2)}GB";
    cpu_threads = threads;
  };

in {
  summary = with project;
    "Project ${name}: ${organism} with ${toString samples} samples";

  resources = with config; {
    inherit memory cpu_threads;
    estimated_time = config.cpu_threads * 2;
  };
}
nix eval -f let_with.nix

실습 5: 실제 환경 구성

# env_config.nix
{ pkgs ? import <nixpkgs> {} }:
let
  python_version = "38";
  use_gpu = false;

  base_packages = ps: with ps; [numpy pandas matplotlib];
  ml_packages = ps: with ps; [scikit-learn];
  gpu_packages = ps: with ps; [tensorflow];

  select_packages = ps:
    base_packages ps ++ ml_packages ps ++
    (if use_gpu then gpu_packages ps else []);

in {
  pythonEnv = pkgs."python${python_version}".withPackages select_packages;

  shellConfig = {
    name = "bioanalysis-env";
    buildInputs = [ pythonEnv ];
    shellHook = ''
      echo "Python ${python_version} environment ready"
      echo "GPU support: ${if use_gpu then "enabled" else "disabled"}"
    '';
  };
}
nix eval -f env_config.nix.shellConfig --json

핵심 정리

기본 문법 요약

# 변수와 함수
let x = 42; f = y: y * 2; in f x

# 조건문
if condition then value1 else value2

# 리스트와 속성집합
[ "a" "b" "c" ]
{ name = "value"; }

# 함수 호출
function argument
function { attr1 = val1; attr2 = val2; }

자주 사용하는 패턴

# 환경 설정 패턴
{ pkgs ? import <nixpkgs> {} }:
let config = { ... };
in pkgs.mkShell { buildInputs = [ ... ]; }

# 조건부 패키지 패턴
packages ++ (if condition then extra_packages else [])

# 속성집합 패턴 매칭
{ name, version ? "1.0", ... }@args:

다음: 2.5 Nix Core Concepts

2.5 Nix Core Concepts and System Architecture

핵심 개념

Nix의 동작 원리는 Derivation 빌드 시스템, 해시 기반 Nix Store, 순수 함수적 의존성 해결을 통해 완전한 재현성과 격리를 보장합니다. 이러한 아키텍처는 생물정보학 연구에서 요구되는 복잡한 소프트웨어 스택의 안정적 관리를 가능하게 합니다.

Derivation: 빌드 명세서

Derivation은 패키지나 환경을 빌드하는 방법을 완전히 기술한 명세서입니다. 모든 입력, 빌드 과정, 예상 출력을 포함하여 동일한 입력에서 항상 동일한 출력을 보장합니다.

# Derivation 구조 확인
$ nix show-derivation -f '<nixpkgs>' python38
{
  "/nix/store/abc123...python3-3.8.16.drv": {
    "outputs": {
      "out": {
        "path": "/nix/store/def456...python3-3.8.16"
      }
    },
    "inputSrcs": [
      "/nix/store/ghi789...Python-3.8.16.tar.xz"
    ],
    "inputDrvs": {
      "/nix/store/jkl012...glibc-2.37-8.drv": ["out"],
      "/nix/store/mno345...openssl-3.0.9.drv": ["out"],
      "/nix/store/pqr678...zlib-1.2.13.drv": ["out"]
    },
    "system": "x86_64-linux",
    "builder": "/nix/store/stu901...bash-5.2-p15/bin/bash",
    "args": ["-e", "/nix/store/vwx234...builder.sh"],
    "env": {
      "buildInputs": "...",
      "nativeBuildInputs": "...",
      "src": "/nix/store/ghi789...Python-3.8.16.tar.xz"
    }
  }
}

Derivation의 핵심 구성 요소:

  • outputs: 빌드 결과물이 저장될 경로들
  • inputSrcs: 소스 파일들 (타르볼, 패치 등)
  • inputDrvs: 의존하는 다른 derivation들
  • system: 대상 플랫폼 (x86_64-linux, aarch64-darwin 등)
  • builder: 빌드를 수행할 프로그램
  • args: 빌더에 전달할 인자들
  • env: 빌드 환경 변수들
# 커스텀 Derivation 생성 예제
{ pkgs ? import <nixpkgs> {} }:

let
  # 간단한 분석 스크립트 패키지
  analysis_script = pkgs.stdenv.mkDerivation {
    pname = "sequence-analyzer";
    version = "1.0.0";

    # 소스 코드 (로컬 파일)
    src = pkgs.writeText "analyze.py" ''
      #!/usr/bin/env python3
      import sys
      import json

      def analyze_fasta(filename):
          sequences = 0
          total_length = 0

          with open(filename) as f:
              for line in f:
                  if line.startswith('>'):
                      sequences += 1
                  else:
                      total_length += len(line.strip())

          return {
              "sequences": sequences,
              "total_length": total_length,
              "average_length": total_length / sequences if sequences > 0 else 0
          }

      if __name__ == "__main__":
          result = analyze_fasta(sys.argv[1])
          print(json.dumps(result, indent=2))
    '';

    # 빌드 의존성
    nativeBuildInputs = with pkgs; [ python3 ];

    # 설치 과정 정의
    installPhase = ''
      mkdir -p $out/bin
      cp $src $out/bin/sequence-analyzer
      chmod +x $out/bin/sequence-analyzer

      # Python 경로 수정
      sed -i '1s|.*|#!${pkgs.python3}/bin/python3|' $out/bin/sequence-analyzer
    '';

    # 메타데이터
    meta = with pkgs.lib; {
      description = "Simple FASTA sequence analyzer";
      platforms = platforms.unix;
    };
  };

in analysis_script

Nix Store: 불변 패키지 저장소

Nix Store는 모든 패키지가 저장되는 중앙 저장소로, 해시 기반 주소 체계를 통해 불변성과 격리를 보장합니다.

# Store 경로 구조 분석
$ ls /nix/store/ | head -5
0123456789abcdef01234567-glibc-2.37-8
1a2b3c4d5e6f7890a1b2c3d4-python3-3.8.16
2b3c4d5e6f789012b2c3d4e5-numpy-1.24.3-python3.8
3c4d5e6f78901234c3d4e5f6-gcc-12.3.0
4d5e6f7890123456d4e5f6g7-openssl-3.0.9

# 경로 구조: /nix/store/[32자리해시]-[패키지명]-[버전]/
# 해시는 모든 입력(소스, 의존성, 빌드 스크립트)으로부터 계산됨

해시 계산과 재현성:

# 동일한 입력에서 동일한 해시 생성 확인
$ nix-instantiate -E 'with import <nixpkgs> {}; python38'
/nix/store/abc123...python3-3.8.16.drv

$ nix-instantiate -E 'with import <nixpkgs> {}; python38'
/nix/store/abc123...python3-3.8.16.drv  # 동일한 경로

# 다른 입력은 다른 해시 생성
$ nix-instantiate -E 'with import <nixpkgs> {}; python39'
/nix/store/xyz789...python3-3.9.18.drv  # 다른 경로

Store 경로의 내용 분석:

# 패키지 내부 구조 확인
$ ls -la /nix/store/abc123...python3-3.8.16/
drwxr-xr-x bin/          # 실행 파일들
drwxr-xr-x lib/          # 라이브러리들
drwxr-xr-x include/      # 헤더 파일들
drwxr-xr-x share/        # 공유 데이터

# 의존성 확인 - 절대 경로로 하드코딩됨
$ ldd /nix/store/abc123...python3-3.8.16/bin/python3
linux-vdso.so.1 => (0x00007fff...)
libc.so.6 => /nix/store/def456...glibc-2.37/lib/libc.so.6
libdl.so.2 => /nix/store/def456...glibc-2.37/lib/libdl.so.2

의존성 추적과 격리

Nix는 각 패키지의 의존성을 명시적으로 추적하고 격리합니다. 이는 "dependency hell" 문제를 근본적으로 해결합니다.

# 의존성 관계 조회
$ nix-store --query --references /nix/store/abc123...python3-3.8.16
/nix/store/def456...glibc-2.37-8
/nix/store/ghi789...openssl-3.0.9
/nix/store/jkl012...zlib-1.2.13
/nix/store/mno345...ncurses-6.4
/nix/store/pqr678...readline-8.2

# 역방향 의존성 (이 패키지를 참조하는 패키지들)
$ nix-store --query --referrers /nix/store/abc123...python3-3.8.16
/nix/store/stu901...python3-numpy-1.24.3
/nix/store/vwx234...python3-pandas-2.0.1

# 의존성 트리 시각화
$ nix-store --query --tree /nix/store/abc123...python3-3.8.16 | head -15
/nix/store/abc123...python3-3.8.16
├───/nix/store/def456...glibc-2.37-8
│   ├───/nix/store/yza567...linux-headers-6.3
│   └───/nix/store/bcd890...gcc-12.3.0-lib
├───/nix/store/ghi789...openssl-3.0.9
│   ├───/nix/store/def456...glibc-2.37-8 [...]
│   └───/nix/store/jkl012...zlib-1.2.13
└───/nix/store/mno345...ncurses-6.4
    └───/nix/store/def456...glibc-2.37-8 [...]

환경 격리 메커니즘:

# 시스템과 완전히 분리된 환경
$ which python
/usr/bin/which: no python in (/usr/bin:/bin)

$ nix-shell -p python38
[nix-shell]$ which python
/nix/store/abc123...python3-3.8.16/bin/python

[nix-shell]$ echo $PATH
/nix/store/abc123...python3-3.8.16/bin:/nix/store/def456...coreutils-9.3/bin:...

[nix-shell]$ exit
$ which python
/usr/bin/which: no python in (/usr/bin:/bin)  # 다시 격리됨

바이너리 캐시와 성능 최적화

Nix는 미리 빌드된 바이너리를 캐시 서버에서 다운로드하여 빌드 시간을 크게 단축합니다. 해시가 같으면 빌드 결과도 동일하므로 안전하게 캐시를 공유할 수 있습니다.

# 캐시 서버 설정 확인
$ nix show-config | grep substituters
substituters = https://cache.nixos.org/ https://nix-community.cachix.org

# 캐시에서 다운로드되는 과정
$ nix-shell -p tensorflow
downloading 'https://cache.nixos.org/nar/0abc123...tensorflow-2.11.0.nar.xz'
downloading 'https://cache.nixos.org/nar/1def456...python3-numpy-1.24.3.nar.xz'
copying path '/nix/store/ghi789...tensorflow-2.11.0' from 'https://cache.nixos.org'...
copying path '/nix/store/jkl012...python3-numpy-1.24.3' from 'https://cache.nixos.org'...

# 로컬 빌드 vs 캐시 다운로드 비교
# 로컬 빌드: TensorFlow 컴파일 시간 2-4시간
# 캐시 다운로드: 네트워크 속도에 따라 5-15분

캐시 작동 원리:

# 빌드 계획 확인 (실제 빌드 전)
$ nix-build -E 'with import <nixpkgs> {}; python38' --dry-run
these derivations will be built:
  /nix/store/abc123...some-dependency.drv
these paths will be fetched (25.4 MiB download, 128.7 MiB unpacked):
  /nix/store/def456...python3-3.8.16
  /nix/store/ghi789...glibc-2.37-8

# 캐시 확인
$ nix path-info --json /nix/store/def456...python3-3.8.16 | jq .narSize
134217728  # 바이트 단위 크기

# 수동 캐시 푸시 (자체 캐시 서버 운영 시)
$ nix copy --to https://my-cache.example.com /nix/store/def456...python3-3.8.16

재현성 보장 메커니즘

Nix의 재현성은 소스 고정, 환경 격리, 결정론적 빌드의 조합으로 달성됩니다.

# 완전한 재현성을 위한 고정된 nixpkgs 사용
{ pkgs ? import (fetchTarball {
    # 특정 커밋 해시로 nixpkgs 고정
    url = "https://github.com/NixOS/nixpkgs/archive/nixos-23.11.tar.gz";
    sha256 = "1wg61h4gndm3vcprdcg7rc4s1v3jkm5xd7lw8r2f67w502y94gcy";
  }) {}
}:

let
  # 정확한 버전 지정
  bioinformatics_env = pkgs.python38.withPackages (ps: with ps; [
    numpy       # 1.24.3 (nixpkgs 23.11에서 고정)
    pandas      # 2.0.3
    biopython   # 1.81
    matplotlib  # 3.7.1
  ]);

in pkgs.mkShell {
  buildInputs = [
    bioinformatics_env
    pkgs.blast     # 2.13.0
    pkgs.bwa       # 0.7.17
    pkgs.samtools  # 1.17
  ];

  # 환경 변수도 고정
  shellHook = ''
    export PYTHONHASHSEED=0  # Python 해시 시드 고정
    export OMP_NUM_THREADS=4 # OpenMP 스레드 수 고정

    echo "생물정보학 환경 (nixpkgs 23.11)"
    echo "Python: $(python --version)"
    echo "NumPy: $(python -c 'import numpy; print(numpy.__version__)')"
  '';
}

재현성 테스트:

# 1년 후에도 동일한 환경 구성 확인
$ nix-shell reproducible-bioinfo.nix --run "python -c 'import numpy; print(numpy.__version__)'"
1.24.3  # 항상 동일한 버전

# 다른 시스템에서도 동일한 결과
$ nix-shell --pure reproducible-bioinfo.nix --run "blast --version"
blastn: 2.13.0+  # 시스템과 무관하게 동일

# 해시 기반 확인성
$ nix-store --query --hash /nix/store/abc123...numpy-1.24.3
sha256:1a2b3c4d5e6f7890...  # 내용이 같으면 해시도 같음

가비지 컬렉션과 스토리지 관리

Nix Store는 자동으로 사용하지 않는 패키지를 정리하여 디스크 공간을 관리합니다.

# 현재 스토어 사용량 확인
$ du -sh /nix/store
45G     /nix/store

# GC 루트 확인 (삭제되지 않을 경로들)
$ nix-store --gc --print-roots | head -5
/home/user/.nix-profile -> /nix/store/abc123...user-environment
/nix/var/nix/profiles/default -> /nix/store/def456...system-profile
/run/current-system -> /nix/store/ghi789...nixos-system

# 삭제 가능한 경로 확인
$ nix-store --gc --print-dead | wc -l
1247  # 1247개 경로가 삭제 가능

# 예상 절약 공간 확인
$ nix-store --gc --print-dead | xargs nix-store --query --size | awk '{sum+=$1} END {print sum/1024/1024/1024 " GB"}'
8.5 GB

# 가비지 컬렉션 실행
$ nix-collect-garbage
deleting '/nix/store/old123...unused-package-1.0'
deleting '/nix/store/old456...temporary-build'
...
8.5G freed

# 특정 기간보다 오래된 것만 삭제
$ nix-collect-garbage --delete-older-than 30d
$ nix-collect-garbage --delete-older-than "2023-01-01"

세대 관리:

# 프로필 세대 확인
$ nix-env --list-generations
   1   2023-10-01 10:00:00   (current)
   2   2023-10-15 14:30:00
   3   2023-11-01 09:15:00

# 특정 세대로 롤백
$ nix-env --switch-generation 2

# 오래된 세대 삭제
$ nix-env --delete-generations old
$ nix-env --delete-generations 1 2  # 특정 세대들만

실제 워크플로우에서의 동작 과정

생물정보학 연구에서 Nix가 어떻게 동작하는지 단계별로 살펴보겠습니다.

# 1. 환경 정의 평가
$ nix-shell bioinfo-env.nix
evaluating derivation '/nix/store/abc123...bioinfo-env.drv'...

# 2. 의존성 그래프 구성
building dependency graph...
- python38: cache hit
- numpy: cache hit
- biopython: cache hit
- blast: needs building

# 3. 캐시 확인 및 다운로드
downloading from 'https://cache.nixos.org'...
copying path '/nix/store/def456...python3-3.8.16' (25.4 MiB)
copying path '/nix/store/ghi789...numpy-1.24.3' (15.2 MiB)

# 4. 필요시 로컬 빌드
building '/nix/store/jkl012...blast-2.13.0.drv'...
unpacking sources...
configuring...
building...
installing...

# 5. 환경 활성화
[nix-shell]$ which python
/nix/store/def456...python3-3.8.16/bin/python

[nix-shell]$ which blastn
/nix/store/jkl012...blast-2.13.0/bin/blastn

Practice Section: 핵심 개념 실습

실습 1: Derivation 구조 확인

# 간단한 패키지의 Derivation 확인
nix show-derivation -f '<nixpkgs>' hello --json | jq '.[] | keys'

# Python 환경의 의존성 확인
nix-instantiate -E 'with import <nixpkgs> {}; python38' | head -1

실습 2: Store 경로 탐색

# Store에서 Python 패키지 찾기
ls /nix/store/*python3* | head -3

# 의존성 관계 확인
PYTHON_PATH=$(nix-build -E 'with import <nixpkgs> {}; python38' --no-out-link)
nix-store --query --references $PYTHON_PATH | head -5

실습 3: 환경 격리 테스트

# 시스템 Python 확인
which python || echo "시스템에 Python 없음"

# Nix 환경에서 Python 사용
nix-shell -p python38 --run "which python && python --version"

# 격리 확인
echo "시스템: $(which python 2>/dev/null || echo 'None')"

실습 4: 재현성 확인

# reproducible_test.nix
{ pkgs ? import (fetchTarball {
    url = "https://github.com/NixOS/nixpkgs/archive/nixos-23.11.tar.gz";
    sha256 = "1wg61h4gndm3vcprdcg7rc4s1v3jkm5xd7lw8r2f67w502y94gcy";
  }) {}
}:
pkgs.mkShell {
  buildInputs = [ pkgs.python38 ];
  shellHook = "python --version";
}
nix-shell reproducible_test.nix

실습 5: 가비지 컬렉션

# 현재 Store 크기 확인
du -sh /nix/store 2>/dev/null || echo "권한 부족"

# 삭제 가능한 경로 개수 확인
nix-store --gc --print-dead | wc -l

# 안전한 가비지 컬렉션 (dry-run)
nix-collect-garbage --dry-run | head -5

핵심 정리

Nix 시스템 아키텍처

사용자 요청 → Derivation 생성 → 의존성 해결 → 캐시 확인 → 빌드/다운로드 → Store 저장 → 환경 활성화

핵심 명령어

# Derivation 관리
nix show-derivation expr         # Derivation 구조 확인
nix-instantiate expr             # .drv 파일 생성

# Store 관리
nix-store --query --references   # 의존성 확인
nix-store --gc                   # 가비지 컬렉션
nix path-info --size             # 크기 정보

# 재현성
nix-shell --pure                 # 순수 환경
nix-build --check               # 재빌드 검증

시스템 이해 핵심

  • 불변성: Store 경로는 절대 변경되지 않음
  • 격리: 각 패키지는 명시적 의존성만 참조
  • 재현성: 같은 입력 → 같은 해시 → 같은 결과
  • 효율성: 바이너리 캐시로 빌드 시간 단축

다음: Chapter 3: Version Control with Git

Chapter 3: Version Control with Git

Git은 분산 버전 관리 시스템으로, 생물정보학 연구에서 코드, 데이터, 논문의 변경 이력을 추적하고 팀 협업을 가능하게 합니다. 특히 대용량 데이터셋과 복잡한 분석 파이프라인을 관리하는 연구 환경에서 필수적인 도구입니다.

학습 목표

  • 버전 관리 개념: 변경 이력 추적과 협업의 필요성 이해
  • Git 워크플로우: Repository, Staging, Commit의 3-영역 모델 습득
  • 브랜치 전략: 기능별 개발과 안전한 병합 관리
  • 원격 협업: GitHub/GitLab을 통한 팀 작업 및 코드 리뷰
  • 데이터 관리: 생물정보학 데이터의 버전 관리 모범 사례

목차


3.1 Git Fundamentals and Workflow

핵심 개념

Git은 스냅샷 기반의 분산 버전 관리 시스템으로, 프로젝트의 전체 이력을 로컬에 저장하여 네트워크 연결 없이도 대부분의 작업을 수행할 수 있습니다. 생물정보학 연구에서는 분석 스크립트, 설정 파일, 결과 데이터의 변경 추적과 협업에 필수적입니다.

Git의 3-영역 모델

Git은 작업 디렉토리(Working Directory), 스테이징 영역(Staging Area), 저장소(Repository)의 3단계 구조로 파일 변경을 관리합니다.

flowchart LR
    A[작업 디렉토리<br/>실제 파일 편집] -->|git add| B[스테이징 영역<br/>커밋 준비 영역]
    B -->|git commit| C[로컬 저장소<br/>영구 저장]
    C -->|git push| D[원격 저장소<br/>협업 공간]

    style A fill:#f9f,stroke:#333,stroke-width:1px
    style B fill:#bbf,stroke:#333,stroke-width:1px
    style C fill:#bfb,stroke:#333,stroke-width:1px
    style D fill:#ffb,stroke:#333,stroke-width:1px

각 영역의 역할과 특징:

  • 작업 디렉토리 (Working Directory): 실제 파일들이 위치하는 공간으로, 파일을 편집하고 수정하는 곳입니다.
  • 스테이징 영역 (Staging Area/Index): 다음 커밋에 포함될 변경사항들을 임시로 저장하는 공간입니다.
  • 로컬 저장소 (Local Repository): 프로젝트의 모든 이력이 저장되는 .git 디렉토리입니다.
  • 원격 저장소 (Remote Repository): GitHub, GitLab 등의 서버에 있는 공유 저장소입니다.
# 파일 상태 확인
$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   analysis.py

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   config.yaml

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        results/new_data.csv

Git 오브젝트 모델

Git은 내부적으로 4가지 타입의 오브젝트를 사용하여 데이터를 저장합니다. 이 구조를 이해하면 Git의 동작 원리를 명확히 파악할 수 있습니다.

# Git 오브젝트 타입들
- Blob: 파일 내용 저장
- Tree: 디렉토리 구조와 파일명 저장
- Commit: 스냅샷과 메타데이터 저장
- Tag: 특정 커밋에 대한 참조

# 커밋 오브젝트 구조 확인
$ git cat-file -p HEAD
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
parent a1b2c3d4e5f6789012345678901234567890abcd
author Researcher <researcher@university.edu> 1640995200 +0900
committer Researcher <researcher@university.edu> 1640995200 +0900

Add RNA-seq analysis pipeline

This commit includes:
- Quality control scripts
- Read alignment pipeline
- Differential expression analysis

해시 기반 무결성:

# 모든 오브젝트는 SHA-1 해시로 식별
$ git log --oneline -3
a1b2c3d (HEAD -> main) Add RNA-seq analysis pipeline
e4f5g6h Update quality control parameters
i7j8k9l Initial project setup

# 해시는 내용의 변경을 즉시 감지
$ echo "Modified content" >> analysis.py
$ git add analysis.py
$ git commit -m "Update analysis"
b2c3d4e (HEAD -> main) Update analysis  # 새로운 해시 생성

기본 설정과 초기화

Git 사용 전 기본 설정을 통해 커밋 작성자 정보와 편집기를 지정해야 합니다.

# 사용자 정보 설정 (필수)
git config --global user.name "Researcher Name"
git config --global user.email "researcher@university.edu"

# 편집기 설정
git config --global core.editor "vim"
git config --global core.editor "code --wait"  # VS Code 사용시

# 기본 브랜치 이름 설정
git config --global init.defaultBranch main

# 줄바꿈 처리 (OS별 다름)
git config --global core.autocrlf input    # Linux/macOS
git config --global core.autocrlf true     # Windows

# 설정 확인
git config --list | grep user
git config --global --list

고급 설정:

# 별칭 설정으로 생산성 향상
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual '!gitk'

# 색상 설정
git config --global color.ui auto
git config --global color.branch auto
git config --global color.diff auto
git config --global color.status auto

# diff 도구 설정
git config --global diff.tool vimdiff
git config --global merge.tool vimdiff

파일 상태와 라이프사이클

Git에서 파일은 추적됨(Tracked)과 추적되지 않음(Untracked) 상태로 구분되며, 추적되는 파일은 다시 수정되지 않음(Unmodified), 수정됨(Modified), 스테이징됨(Staged) 상태를 가집니다.

flowchart LR
    Untracked -- git add --> Staged
    Staged -- git commit --> Unmodified
    Unmodified -- edit file --> Modified
    Modified -- git add --> Staged
    Staged -- edit file --> Modified
    Modified -- remove file --> Untracked

    style Untracked fill:#fdd,stroke:#333,stroke-width:1px
    style Staged fill:#bbf,stroke:#333,stroke-width:1px
    style Unmodified fill:#dfd,stroke:#333,stroke-width:1px
    style Modified fill:#ffd,stroke:#333,stroke-width:1px

상태별 명령어:

# 파일 상태 확인
git status                          # 전체 상태 요약
git status --short                  # 간단한 형태
git status --porcelain             # 스크립트용 형태

# 파일별 상태 기호 의미
# ?? : Untracked
# A  : Added (새 파일이 스테이징됨)
# M  : Modified (기존 파일이 수정됨)
# D  : Deleted (파일이 삭제됨)
# R  : Renamed (파일명이 변경됨)
# C  : Copied (파일이 복사됨)

# 변경 내용 확인
git diff                           # 작업 디렉토리 vs 스테이징 영역
git diff --staged                  # 스테이징 영역 vs 최신 커밋
git diff HEAD                      # 작업 디렉토리 vs 최신 커밋
git diff --name-only              # 변경된 파일명만 표시

커밋 메시지 작성 규칙

생물정보학 연구에서는 분석 과정과 변경 이유를 명확히 기록하는 것이 중요합니다. 일관성 있는 커밋 메시지 규칙을 따르면 프로젝트 이력을 효과적으로 관리할 수 있습니다.

# 좋은 커밋 메시지 구조
<type>(<scope>): <subject>

<body>

<footer>

# 예시:
feat(pipeline): Add quality control step for RNA-seq data

- Implement FastQC integration for read quality assessment
- Add trimming step using Trimmomatic
- Update pipeline configuration for batch processing

Closes #123

커밋 타입 분류:

# 주요 커밋 타입들
feat:     새로운 기능 추가
fix:      버그 수정
docs:     문서 변경
style:    코드 스타일 변경 (포매팅, 세미콜론 등)
refactor: 코드 리팩토링 (기능 변경 없음)
test:     테스트 추가 또는 수정
chore:    빌드 과정 또는 보조 도구 변경
perf:     성능 개선
data:     데이터 파일 추가 또는 변경 (생물정보학 특화)
config:   설정 파일 변경
analysis: 분석 방법 또는 매개변수 변경

# 실제 사용 예시
git commit -m "feat(alignment): Add BWA-MEM alignment pipeline"
git commit -m "fix(qc): Correct quality score calculation in FastQC parser"
git commit -m "data(samples): Add control group RNA-seq data (n=12)"
git commit -m "config(cluster): Update SLURM job parameters for large datasets"
git commit -m "analysis(deseq): Adjust p-value threshold to 0.01"

이력 조회와 탐색

프로젝트의 변경 이력을 효과적으로 탐색하는 것은 연구 과정 추적과 문제 해결에 필수적입니다.

# 기본 로그 조회
git log                            # 전체 커밋 이력
git log --oneline                  # 한 줄씩 간단히 표시
git log --graph                    # 브랜치 그래프와 함께
git log --all                      # 모든 브랜치 포함

# 특정 조건으로 필터링
git log --since="2 weeks ago"      # 2주 이내 커밋
git log --until="2023-12-31"       # 특정 날짜 이전
git log --author="Researcher"      # 특정 작성자
git log --grep="RNA-seq"           # 메시지에 키워드 포함
git log -S "function_name"         # 코드에서 특정 문자열 변경

# 파일별 이력
git log --follow path/to/file      # 파일명 변경 추적
git log -p path/to/file           # 파일 변경 내용과 함께
git log --stat                     # 변경 파일 통계와 함께

# 고급 포맷팅
git log --pretty=format:"%h %an %ar %s"
git log --pretty=format:"%C(yellow)%h%C(reset) %C(blue)%an%C(reset) %C(green)%ar%C(reset) %s"

특정 변경사항 추적:

# 특정 함수나 코드 블록의 변경 이력
git log -L :function_name:script.py         # 함수별 변경 이력
git log -L 10,20:config.yaml               # 특정 라인 범위 변경 이력

# 파일 이동 및 복사 추적
git log --follow --find-renames=40% file.py    # 40% 이상 유사하면 이름 변경으로 간주
git log --find-copies-harder                   # 복사된 파일도 추적

# blame을 통한 라인별 작성자 확인
git blame analysis.py                          # 각 라인의 마지막 수정자
git blame -L 10,20 analysis.py               # 특정 라인 범위만
git blame -w analysis.py                      # 공백 변경 무시

Practice Section: Git 기초 실습

실습 1: Git 초기 설정

# Git 설정 확인 및 구성
git config --global user.name "Bio Researcher"
git config --global user.email "researcher@biolab.edu"

# 별칭 설정
git config --global alias.st "status --short"
git config --global alias.lg "log --oneline --graph"

# 설정 확인
git config --list | grep user

실습 2: 저장소 초기화와 첫 커밋

cd ~/bioproject

# Git 저장소 초기화
git init
git status

# README 파일 생성
echo "# Bioinformatics Analysis Project" > README.md
git add README.md
git commit -m "feat: Initial project setup with README"

실습 3: 파일 상태 추적

# 새 파일 생성
echo "import pandas as pd" > analysis.py
echo "samples = 24" > config.py

# 상태 확인
git status
git status --short

# 파일 스테이징과 커밋
git add analysis.py
git status
git commit -m "feat(analysis): Add basic analysis script"

실습 4: 변경사항 확인

# 파일 수정
echo "import numpy as np" >> analysis.py
echo "samples = 48" > config.py

# 변경사항 확인
git diff
git diff --name-only

# 부분적 스테이징
git add analysis.py
git status
git diff --staged

실습 5: 커밋 이력 확인

# 이력 조회
git log
git log --oneline
git log --stat

# 특정 파일 이력
git log analysis.py
git log --follow analysis.py

핵심 정리

Git 기본 워크플로우

# 표준 작업 순서
git status          # 현재 상태 확인
git add <files>     # 변경사항 스테이징
git commit -m "msg" # 커밋 생성
git log            # 이력 확인

필수 명령어

# 설정
git config --global user.name "Name"
git config --global user.email "email"

# 기본 작업
git init           # 저장소 초기화
git add <file>     # 파일 스테이징
git commit -m      # 커밋 생성
git status         # 상태 확인
git log           # 이력 조회
git diff          # 변경사항 확인

다음: 3.2 Repository Management

3.2 Repository Management

핵심 개념

Repository는 프로젝트의 모든 파일과 변경 이력이 저장되는 공간입니다. 생물정보학 연구에서는 소스 코드, 설정 파일, 스크립트는 추적하되 대용량 데이터나 임시 결과 파일은 제외하는 전략적 관리가 필요합니다. 효과적인 저장소 관리는 협업 효율성과 프로젝트 유지보수성을 크게 좌우합니다.

저장소 생성과 복제

새로운 프로젝트 시작 시 로컬에서 저장소를 초기화하거나, 기존 원격 저장소를 복제하는 두 가지 방법이 있습니다.

# 로컬 저장소 초기화
mkdir rna-seq-analysis
cd rna-seq-analysis
git init
git branch -M main                    # 기본 브랜치를 main으로 설정

# 초기화 확인
ls -la                               # .git 디렉토리 생성 확인
git status                           # 저장소 상태 확인

# 빈 저장소에 첫 커밋 생성
echo "# RNA-seq Analysis Pipeline" > README.md
git add README.md
git commit -m "feat: Initial project setup"

원격 저장소 복제:

# 기본 복제
git clone https://github.com/user/bioproject.git
git clone git@github.com:user/bioproject.git    # SSH 사용

# 복제 옵션들
git clone --depth 1 https://github.com/user/large-project.git      # 최신 커밋만 (얕은 복제)
git clone --branch develop https://github.com/user/project.git     # 특정 브랜치만
git clone --recursive https://github.com/user/project.git          # 서브모듈 포함

# 복제 후 상태 확인
cd bioproject
git remote -v                        # 원격 저장소 확인
git branch -a                        # 모든 브랜치 확인
git log --oneline -5                 # 최근 5개 커밋 확인

저장소 구조 설정:

# 생물정보학 프로젝트 표준 구조 생성
mkdir -p {data/{raw,processed,results},scripts,docs,config,logs}
touch data/raw/.gitkeep              # 빈 디렉토리 추적용
touch data/processed/.gitkeep
touch logs/.gitkeep

# 구조 확인
tree -a                              # 숨김 파일 포함 트리 구조

.gitignore 파일 관리

.gitignore 파일은 Git이 추적하지 않을 파일과 디렉토리를 지정합니다. 생물정보학 프로젝트에서는 대용량 데이터 파일, 임시 결과, 시스템 파일을 제외하는 것이 중요합니다.

# 생물정보학용 .gitignore 생성
cat > .gitignore << 'EOF'
# 대용량 데이터 파일
*.fastq
*.fastq.gz
*.fq
*.fq.gz
*.fasta.gz
*.fa.gz
*.bam
*.sam
*.bed.gz

# 압축 파일
*.zip
*.tar.gz
*.tar.bz2
*.7z

# 결과 및 임시 파일
results/
output/
temp/
tmp/
*.tmp
*.temp
*.out
*.log

# Python 관련
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
env/
venv/
.venv/
.ipynb_checkpoints/

# R 관련
.Rproj.user/
.Rhistory
.RData
.Ruserdata
*.Rproj

# 설정 파일 (민감한 정보 포함)
config/secrets.yaml
config/database.conf
.env
*.key

# IDE 파일
.vscode/
.idea/
*.swp
*.swo
*~

# OS 생성 파일
.DS_Store
Thumbs.db
.directory

# 클러스터 관련
*.o
*.e
slurm-*.out
EOF

.gitignore 패턴 문법:

# 패턴 규칙들
*.log                    # 모든 .log 파일
/logs                    # 루트의 logs 디렉토리만
logs/                    # 모든 logs 디렉토리
temp/                    # temp 디렉토리와 그 내용
!important.log           # 예외 (important.log는 추적)
data/**/*.bam            # data 하위 모든 .bam 파일
results/[0-9]*/          # results/001/, results/002/ 등

# 복잡한 패턴 예시
# 모든 .fastq 파일을 제외하되, 샘플 파일은 포함
*.fastq
!sample*.fastq

# 특정 확장자를 가진 대용량 파일만 제외
*.[0-9][0-9][0-9]MB.*   # 100MB 이상을 나타내는 파일명 패턴

기존 추적 파일 제거:

# 이미 추적 중인 파일을 .gitignore에 추가한 경우
git rm --cached large_data.bam       # 추적에서만 제거 (파일은 보존)
git rm -r --cached results/         # 디렉토리 전체 추적 제거

# .gitignore 적용 확인
git status                           # 더 이상 추적되지 않는지 확인
git commit -m "chore: Remove large data files from tracking"

파일 조작과 스테이징

Git에서 파일을 추가, 수정, 삭제, 이동할 때는 각 작업의 특성을 이해하고 적절한 명령어를 사용해야 합니다.

# 파일 추가 (다양한 방법)
git add analysis.py                  # 단일 파일
git add scripts/                     # 디렉토리 전체
git add *.py                         # 패턴 매칭
git add .                           # 현재 디렉토리 모든 변경사항
git add -A                          # 저장소 전체 모든 변경사항

# 선택적 스테이징
git add -p analysis.py              # 파일 내 일부분만 선택적으로 스테이징
git add -i                          # 대화형 스테이징

# 파일 제거
git rm obsolete_script.py           # 파일 삭제 및 스테이징
git rm --cached config/secrets.yaml # 추적에서만 제거 (파일은 보존)
git rm -r old_results/              # 디렉토리 삭제

# 파일 이동 및 이름 변경
git mv old_name.py new_name.py      # 파일 이름 변경
git mv scripts/analysis.py bin/    # 파일 이동

고급 스테이징 기법:

# 부분적 스테이징 (hunk 단위)
git add -p script.py
# y: 현재 hunk 스테이징
# n: 현재 hunk 건너뛰기
# s: hunk를 더 작은 단위로 분할
# e: hunk를 수동으로 편집

# 대화형 스테이징
git add -i
# 1: status        - 파일 상태 확인
# 2: update        - 파일 스테이징
# 3: revert        - 스테이징 취소
# 4: add untracked - 새 파일 추가
# 5: patch         - 부분적 스테이징
# 6: diff          - 차이점 확인

# 스테이징 영역 조작
git reset HEAD analysis.py          # 특정 파일 스테이징 취소
git reset HEAD                      # 모든 스테이징 취소
git restore --staged analysis.py    # Git 2.23+ 새로운 문법

변경사항 되돌리기

실수로 만든 변경사항이나 잘못된 커밋을 되돌리는 것은 개발 과정에서 자주 필요한 작업입니다.

# 작업 디렉토리 변경사항 되돌리기
git restore analysis.py             # 파일을 최신 커밋 상태로 되돌리기
git restore .                       # 모든 변경사항 되돌리기
git checkout HEAD -- analysis.py   # 구버전 Git에서 사용

# 특정 커밋으로 파일 되돌리기
git restore --source=HEAD~2 analysis.py    # 2개 커밋 전 상태로
git checkout abc1234 -- analysis.py        # 특정 커밋 해시로

# 커밋 수정과 되돌리기
git commit --amend                  # 마지막 커밋 메시지나 내용 수정
git commit --amend --no-edit        # 메시지 변경 없이 파일만 추가

# 커밋 되돌리기 (3가지 방법)
git reset --soft HEAD~1             # 커밋만 취소, 변경사항은 스테이징 상태로 유지
git reset --mixed HEAD~1            # 커밋과 스테이징 취소, 변경사항은 작업 디렉토리에 유지 (기본값)
git reset --hard HEAD~1             # 커밋, 스테이징, 작업 디렉토리 모든 변경사항 취소

# 특정 커밋 되돌리기 (안전한 방법)
git revert abc1234                  # 특정 커밋의 변경사항을 취소하는 새 커밋 생성
git revert HEAD~2..HEAD             # 범위로 여러 커밋 되돌리기

위험한 작업 전 백업:

# 현재 상태 백업
git stash push -m "Backup before dangerous operation"
git branch backup-$(date +%Y%m%d-%H%M%S)

# 또는 태그로 백업
git tag backup-before-reset

# 작업 후 복구가 필요한 경우
git reset --hard backup-before-reset
git stash pop

원격 저장소 관리

원격 저장소는 협업의 중심이며, 여러 원격 저장소를 관리하는 능력은 복잡한 프로젝트에서 필수적입니다.

# 원격 저장소 확인
git remote -v                       # 모든 원격 저장소 URL과 함께 표시
git remote show origin              # 특정 원격 저장소 상세 정보

# 원격 저장소 추가
git remote add upstream https://github.com/original/project.git
git remote add backup https://github.com/backup/project.git

# 원격 저장소 수정
git remote set-url origin https://github.com/newuser/project.git
git remote rename origin old-origin
git remote remove backup

# 원격 브랜치 정보 업데이트
git fetch origin                    # origin에서 최신 정보 가져오기
git fetch --all                     # 모든 원격 저장소에서 가져오기
git fetch --prune                   # 삭제된 원격 브랜치 정보도 동기화

원격 저장소와 동기화:

# 첫 푸시 (원격 브랜치 생성)
git push -u origin main             # 업스트림 설정과 함께 푸시

# 일반적인 푸시/풀
git push                            # 현재 브랜치를 추적하는 원격 브랜치로 푸시
git pull                            # 원격에서 변경사항 가져와 자동 병합
git pull --rebase                   # rebase 방식으로 통합

# 강제 푸시 (위험하므로 주의)
git push --force-with-lease         # 안전한 강제 푸시
git push --force                    # 위험한 강제 푸시 (팀 작업에서 금지)

# 특정 브랜치 작업
git push origin feature-branch      # 특정 브랜치 푸시
git push origin --delete old-branch # 원격 브랜치 삭제

스테이징 영역 고급 활용

스테이징 영역을 효과적으로 활용하면 논리적으로 관련된 변경사항을 그룹화하여 의미 있는 커밋을 만들 수 있습니다.

# 스테이징 상태 세밀 조정
git diff --name-status              # 변경된 파일과 상태 요약
git diff --cached                   # 스테이징된 변경사항만 확인
git diff HEAD                       # 작업 디렉토리와 HEAD 비교

# 파일별 다른 처리
git add scripts/                    # 스크립트 파일들 스테이징
git add config/pipeline.yaml       # 설정 파일 스테이징
# 데이터 파일들은 스테이징하지 않음

# 스테이징 영역에서 일부만 제거
git reset HEAD config/secrets.yaml # 민감한 파일 스테이징 취소

# 스테이지된 변경사항 임시 저장
git stash --keep-index              # 스테이지되지 않은 변경사항만 stash
git stash --include-untracked       # 추적되지 않는 파일도 포함

Practice Section: 저장소 관리 실습

실습 1: 프로젝트 구조 설정

cd ~/bioproject

# 표준 디렉토리 구조 생성
mkdir -p {data/{raw,processed},scripts,results,config}
touch data/raw/.gitkeep
touch results/.gitkeep

# .gitignore 생성
echo "*.fastq\n*.bam\nresults/*.csv\n__pycache__/" > .gitignore

git add .
git commit -m "feat: Setup project structure with gitignore"

실습 2: 파일 추가와 관리

# 스크립트 파일 생성
echo "#!/usr/bin/env python3\nprint('Hello Bio!')" > scripts/hello.py
echo "samples: 24\nthreads: 8" > config/settings.yaml

# 선택적 추가
git add scripts/
git status
git commit -m "feat(scripts): Add hello script"

git add config/settings.yaml
git commit -m "config: Add pipeline settings"

실습 3: 변경사항 되돌리기

# 파일 수정
echo "import pandas as pd" >> scripts/hello.py
echo "debug: true" >> config/settings.yaml

# 일부 변경사항만 되돌리기
git restore scripts/hello.py
git status

# 나머지 변경사항 커밋
git add config/settings.yaml
git commit -m "config: Enable debug mode"

실습 4: 스테이징 조작

# 여러 파일 수정
echo "# Analysis Results" > results/summary.md
echo "import numpy as np" >> scripts/hello.py
echo "version: 2.0" >> config/settings.yaml

# 선택적 스테이징
git add results/summary.md config/settings.yaml
git status

# 일부만 커밋
git commit -m "docs: Add results summary and update config"

실습 5: 원격 저장소 설정

# GitHub 저장소와 연결 (실제 URL 필요)
# git remote add origin https://github.com/username/bioproject.git

# 원격 저장소 확인
git remote -v

# 로컬 작업 푸시 준비
git branch -M main
echo "Repository ready for remote push"

핵심 정리

저장소 관리 필수 명령어

# 초기 설정
git init                    # 저장소 초기화
git clone <url>            # 원격 저장소 복제

# 파일 관리
git add <file>             # 파일 스테이징
git rm <file>              # 파일 삭제
git mv <old> <new>         # 파일 이동/이름변경

# 변경사항 되돌리기
git restore <file>         # 작업 디렉토리 되돌리기
git reset HEAD <file>      # 스테이징 취소
git commit --amend         # 마지막 커밋 수정

생물정보학 프로젝트 관리 패턴

# .gitignore 필수 항목
*.fastq *.bam *.sam        # 대용량 시퀀싱 데이터
results/ output/           # 결과 디렉토리
*.log *.tmp               # 임시 파일
__pycache__/              # Python 캐시

# 디렉토리 구조
data/{raw,processed}/     # 데이터 분리
scripts/                  # 분석 스크립트
config/                   # 설정 파일
results/                  # 결과 (추적 제외)

다음: 3.3 Branching and Merging

3.3 Branching and Merging

핵심 개념

브랜치는 Git의 가장 강력한 기능 중 하나로, 메인 개발 라인에서 분기하여 독립적인 작업을 수행할 수 있게 합니다. 생물정보학 연구에서는 실험적 분석 방법, 매개변수 조정, 새로운 알고리즘 테스트를 위해 브랜치를 활용하여 안정적인 메인 코드를 보호하면서 혁신적인 시도를 할 수 있습니다.

브랜치 모델과 동작 원리

Git의 브랜치는 커밋을 가리키는 가벼운 포인터입니다. 새로운 브랜치를 생성해도 저장소 크기가 거의 증가하지 않으며, 브랜치 간 전환도 매우 빠릅니다.

# 브랜치 구조 시각화
$ git log --oneline --graph --all
* a1b2c3d (HEAD -> feature-qc) feat(qc): Add advanced quality metrics
* e4f5g6h (main) fix(pipeline): Correct alignment parameters
* i7j8k9l feat(data): Add sample metadata processing
* m1n2o3p Initial commit

# 브랜치는 단순히 커밋 해시를 가리키는 파일
$ cat .git/refs/heads/main
e4f5g6h1234567890abcdef1234567890abcdef12

$ cat .git/refs/heads/feature-qc
a1b2c3d1234567890abcdef1234567890abcdef12

HEAD 포인터의 역할:

# HEAD는 현재 작업 중인 브랜치를 가리킴
$ cat .git/HEAD
ref: refs/heads/feature-qc

# 브랜치 전환 시 HEAD가 이동
$ git checkout main
$ cat .git/HEAD
ref: refs/heads/main

# detached HEAD 상태 (특정 커밋으로 이동)
$ git checkout a1b2c3d
$ cat .git/HEAD
a1b2c3d1234567890abcdef1234567890abcdef12

브랜치 생성과 관리

효과적인 브랜치 관리는 연구 프로젝트의 복잡도와 팀 규모에 따라 달라집니다. 생물정보학 프로젝트에서는 분석 방법별, 데이터셋별, 또는 연구 단계별로 브랜치를 구성할 수 있습니다.

# 브랜치 생성 방법들
git branch feature-alignment          # 브랜치 생성 (전환하지 않음)
git checkout -b feature-alignment     # 브랜치 생성 후 전환
git switch -c feature-alignment       # Git 2.23+ 새로운 문법

# 특정 시점에서 브랜치 생성
git checkout -b hotfix-bug main       # main에서 분기
git checkout -b experiment-v2 a1b2c3d # 특정 커밋에서 분기

# 브랜치 목록 확인
git branch                            # 로컬 브랜치만
git branch -a                         # 로컬과 원격 브랜치 모두
git branch -v                         # 마지막 커밋 정보와 함께
git branch --merged                   # 병합된 브랜치들
git branch --no-merged                # 병합되지 않은 브랜치들

브랜치 명명 규칙:

# 생물정보학 프로젝트 브랜치 명명 예시
feature/rna-seq-pipeline             # 새 기능 개발
bugfix/alignment-memory-leak         # 버그 수정
experiment/deep-learning-model       # 실험적 기능
analysis/patient-cohort-2024         # 특정 분석
data/gtex-v8-integration            # 데이터 통합
config/cluster-optimization          # 설정 개선

# 개인 작업 브랜치 (팀 작업 시)
john/methylation-analysis
mary/quality-control-update

브랜치 전환과 상태 관리:

# 안전한 브랜치 전환
git status                           # 현재 변경사항 확인
git stash                           # 작업 중인 변경사항 임시 저장
git checkout target-branch          # 브랜치 전환
git stash pop                       # 저장된 변경사항 복원

# 변경사항이 있어도 강제 전환 (주의 필요)
git checkout -f target-branch       # 변경사항 버리고 전환

# 현재 브랜치 확인
git branch --show-current           # Git 2.22+
git rev-parse --abbrev-ref HEAD     # 구버전에서 사용

머지 전략과 방법

Git은 세 가지 주요 머지 전략을 제공합니다: Fast-forward, Recursive (3-way merge), 그리고 Rebase. 각 전략은 서로 다른 상황과 팀 정책에 적합합니다.

# Fast-forward 머지 (선형 이력 유지)
# main: A---B
# feature:    C---D
# 결과: A---B---C---D (main, feature)

git checkout main
git merge feature-branch            # Fast-forward 가능 시 자동 적용
git merge --ff-only feature-branch # Fast-forward만 허용

# 3-way 머지 (병합 커밋 생성)
# main: A---B---E
# feature:    C---D
# 결과: A---B---E---F (F는 병합 커밋)
#            \     /
#             C---D

git merge --no-ff feature-branch    # 항상 병합 커밋 생성
git merge -m "Merge feature-qc into main" feature-qc

리베이스를 통한 선형 이력 유지:

# 리베이스 (커밋을 다른 베이스로 이동)
# Before:
# main: A---B---E
# feature:    C---D
# After rebase:
# main: A---B---E
# feature:         C'---D' (C, D가 E 이후로 이동)

git checkout feature-branch
git rebase main                     # feature 브랜치를 main 위로 재배치

# 대화형 리베이스 (커밋 편집)
git rebase -i HEAD~3                # 최근 3개 커밋 편집
git rebase -i main                  # main 이후의 모든 커밋 편집

# 리베이스 중 충돌 해결
git add resolved-file.py
git rebase --continue               # 리베이스 계속
git rebase --abort                  # 리베이스 취소

스쿼시 머지 (여러 커밋을 하나로 압축):

# 방법 1: 머지 시 스쿼시
git checkout main
git merge --squash feature-branch   # 모든 변경사항을 스테이징만
git commit -m "feat: Add quality control pipeline"

# 방법 2: 대화형 리베이스로 스쿼시
git checkout feature-branch
git rebase -i HEAD~4                # 최근 4개 커밋 압축
# 편집기에서 'pick' → 'squash' 또는 's'로 변경

충돌 해결

머지나 리베이스 과정에서 같은 파일의 같은 부분이 서로 다르게 수정된 경우 충돌이 발생합니다. 충돌 해결은 Git 사용에서 피할 수 없는 과정입니다.

# 충돌 발생 상황
$ git merge feature-alignment
Auto-merging scripts/pipeline.py
CONFLICT (content): Merge conflict in scripts/pipeline.py
Automatic merge failed; fix conflicts and then commit the result.

# 충돌 상태 확인
$ git status
Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   scripts/pipeline.py

충돌 표시 형식:

# scripts/pipeline.py 파일 내용
def run_alignment(reads, reference):
    """RNA-seq read alignment function"""
<<<<<<< HEAD
    # Use STAR aligner with default parameters
    aligner = "STAR"
    params = {"threads": 8, "memory": "32G"}
=======
    # Use HISAT2 aligner for better performance
    aligner = "HISAT2"
    params = {"threads": 16, "memory": "16G"}
>>>>>>> feature-alignment

    return align_reads(reads, reference, aligner, params)

충돌 해결 과정:

# 1. 충돌 파일 편집 (마커 제거 후 최종 버전 작성)
# 편집 후:
def run_alignment(reads, reference):
    """RNA-seq read alignment function"""
    # Use HISAT2 aligner with optimized parameters
    aligner = "HISAT2"
    params = {"threads": 12, "memory": "24G"}

    return align_reads(reads, reference, aligner, params)

# 2. 해결된 파일 스테이징
git add scripts/pipeline.py

# 3. 충돌 해결 완료
git commit                          # 기본 머지 메시지 사용
# 또는
git commit -m "Merge: Integrate HISAT2 alignment with optimized params"

충돌 해결 도구 활용:

# 3-way 머지 도구 사용
git mergetool                       # 설정된 도구로 충돌 해결
git mergetool --tool=vimdiff        # 특정 도구 지정

# Git 내장 도구로 충돌 확인
git diff                            # 충돌 상태의 diff 표시
git log --merge                     # 충돌 관련 커밋만 표시
git show :1:filename                # 공통 조상 버전
git show :2:filename                # HEAD (현재 브랜치) 버전
git show :3:filename                # 머지하려는 브랜치 버전

# 충돌 해결 취소
git merge --abort                   # 머지 취소하고 이전 상태로
git reset --hard HEAD               # 모든 변경사항 취소

브랜치 워크플로우 패턴

팀의 규모와 프로젝트 특성에 따라 다양한 브랜치 워크플로우를 채택할 수 있습니다. 생물정보학 연구에서는 분석의 안정성과 재현성이 중요하므로 적절한 워크플로우 선택이 필수적입니다.

GitHub Flow (단순한 브랜치 모델):

# 1. 메인 브랜치에서 기능 브랜치 생성
git checkout main
git pull origin main
git checkout -b analysis/deseq2-update

# 2. 작업 후 푸시
git add -A
git commit -m "feat(analysis): Update DESeq2 analysis pipeline"
git push origin analysis/deseq2-update

# 3. Pull Request 생성 및 검토
# (GitHub 웹 인터페이스에서)

# 4. 검토 완료 후 메인으로 머지
git checkout main
git pull origin main              # 최신 상태로 업데이트
git branch -d analysis/deseq2-update  # 로컬 브랜치 삭제

Git Flow (복잡한 프로젝트용):

# 브랜치 구조:
# main: 프로덕션 릴리스
# develop: 개발 통합 브랜치
# feature/*: 기능 개발
# release/*: 릴리스 준비
# hotfix/*: 긴급 수정

# 새 기능 개발
git checkout develop
git checkout -b feature/rna-seq-v2
# ... 개발 작업 ...
git checkout develop
git merge --no-ff feature/rna-seq-v2
git branch -d feature/rna-seq-v2

# 릴리스 준비
git checkout develop
git checkout -b release/v2.1.0
# ... 버그 수정, 문서 업데이트 ...
git checkout main
git merge --no-ff release/v2.1.0
git tag v2.1.0
git checkout develop
git merge --no-ff release/v2.1.0

연구 프로젝트용 워크플로우:

# 브랜치 구조:
# main: 검증된 안정 버전
# develop: 개발 통합
# experiment/*: 실험적 분석
# data/*: 새로운 데이터셋 처리
# paper/*: 논문별 분석

# 실험적 분석 브랜치
git checkout develop
git checkout -b experiment/machine-learning-classifier

# 논문용 분석 브랜치 (특정 시점에서 분기)
git checkout -b paper/nature-genetics-2024 v2.1.0
# ... 논문에 사용된 정확한 분석 보존 ...

# 데이터셋별 브랜치
git checkout -b data/tcga-integration develop

원격 브랜치 관리

팀 협업에서는 로컬 브랜치와 원격 브랜치 간의 동기화가 중요합니다.

# 원격 브랜치 확인
git branch -r                       # 원격 브랜치만
git branch -a                       # 로컬과 원격 모두
git remote show origin              # 원격 저장소 상세 정보

# 원격 브랜치 추적
git checkout -b local-branch origin/remote-branch    # 원격 브랜치 추적
git checkout --track origin/remote-branch           # 동일한 이름으로 추적
git branch -u origin/remote-branch local-branch     # 기존 브랜치에 추적 설정

# 원격 브랜치 동기화
git fetch origin                    # 원격 정보 업데이트
git fetch --prune                   # 삭제된 원격 브랜치 정보 정리
git pull origin main                # 특정 브랜치 가져와 머지
git push origin feature-branch      # 로컬 브랜치를 원격으로 푸시

# 원격 브랜치 삭제
git push origin --delete old-feature    # 원격 브랜치 삭제
git branch -d old-feature               # 로컬 브랜치 삭제

Practice Section: 브랜치와 머지 실습

실습 1: 기본 브랜치 생성과 전환

cd ~/bioproject

# 새 기능 브랜치 생성
git checkout -b feature-quality-control
git branch --show-current

# 간단한 작업
echo "def quality_check(): pass" > scripts/qc.py
git add scripts/qc.py
git commit -m "feat(qc): Add quality control module"

# 메인 브랜치로 돌아가기
git checkout main
git branch -v

실습 2: Fast-forward 머지

# 메인에서 기능 브랜치 머지
git merge feature-quality-control
git log --oneline -3

# 브랜치 정리
git branch -d feature-quality-control
git branch

실습 3: 3-way 머지 시나리오

# 메인에서 추가 작업
echo "version = '1.1'" > scripts/version.py
git add scripts/version.py
git commit -m "feat: Add version information"

# 새 브랜치에서 다른 작업
git checkout -b feature-config
echo "config = {'debug': True}" > scripts/config.py
git add scripts/config.py
git commit -m "feat: Add configuration module"

# 메인으로 돌아가서 머지
git checkout main
git merge --no-ff feature-config -m "Merge config feature"
git log --oneline --graph -5

실습 4: 충돌 해결 연습

# 두 브랜치에서 같은 파일 수정
git checkout -b branch-a
echo "method = 'fastqc'" > scripts/qc.py
git add scripts/qc.py
git commit -m "Use FastQC method"

git checkout main
echo "method = 'multiqc'" > scripts/qc.py
git add scripts/qc.py
git commit -m "Use MultiQC method"

# 충돌 머지 시도
git merge branch-a
# 충돌 해결 후:
echo "method = 'fastqc_and_multiqc'" > scripts/qc.py
git add scripts/qc.py
git commit -m "Merge: Use both QC methods"

실습 5: 브랜치 정리

# 브랜치 현황 확인
git branch -v
git branch --merged

# 불필요한 브랜치 삭제
git branch -d branch-a
git branch -d feature-config

# 최종 상태 확인
git log --oneline --graph
git branch

핵심 정리

브랜치 관리 필수 명령어

# 브랜치 생성/전환
git checkout -b new-branch     # 생성 후 전환
git switch -c new-branch       # Git 2.23+ 새 문법
git branch new-branch          # 생성만

# 머지
git merge branch-name          # 브랜치 머지
git merge --no-ff branch       # 항상 머지 커밋 생성
git merge --squash branch      # 스쿼시 머지

# 브랜치 정리
git branch -d branch-name      # 브랜치 삭제
git branch --merged            # 머지된 브랜치 확인

생물정보학 브랜치 전략

# 브랜치 명명 규칙
feature/pipeline-optimization  # 기능 개발
experiment/deep-learning       # 실험적 분석
data/gtex-integration         # 데이터 통합
paper/nature-2024             # 논문별 분석
bugfix/memory-leak            # 버그 수정

# 안전한 머지 패턴
git checkout main && git pull  # 최신 상태 확인
git merge --no-ff feature     # 머지 이력 보존
git branch -d feature         # 정리

다음: 3.4 Remote Collaboration

3.4 Remote Collaboration

핵심 개념

원격 협업은 분산된 팀이 공통의 코드베이스에서 효율적으로 작업할 수 있게 하는 Git의 핵심 기능입니다. 생물정보학 연구에서는 다기관 협업, 오픈소스 도구 기여, 재현 가능한 연구를 위해 GitHub, GitLab 등의 플랫폼을 통한 체계적인 협업이 필수적입니다.

GitHub/GitLab 플랫폼 이해

GitHub과 GitLab은 Git 저장소 호스팅 서비스이면서 동시에 협업 플랫폼입니다. 단순한 코드 저장을 넘어 이슈 추적, 프로젝트 관리, CI/CD, 문서화 등의 기능을 통합적으로 제공합니다.

# 원격 저장소 설정 (GitHub 예시)
git remote add origin https://github.com/biolab/rna-seq-pipeline.git
git remote add upstream https://github.com/original/rna-seq-pipeline.git

# SSH 키 설정으로 인증 간소화
ssh-keygen -t ed25519 -C "researcher@university.edu"
cat ~/.ssh/id_ed25519.pub  # 공개키를 GitHub에 등록

# SSH URL 사용
git remote set-url origin git@github.com:biolab/rna-seq-pipeline.git

플랫폼별 특징과 활용:

GitHub:

  • 최대 규모의 오픈소스 커뮤니티
  • GitHub Actions을 통한 CI/CD
  • GitHub Pages로 프로젝트 문서 호스팅
  • Codespaces를 통한 클라우드 개발환경

GitLab:

  • 내장된 CI/CD 파이프라인
  • 컨테이너 레지스트리 제공
  • 자체 호스팅 옵션 (GitLab CE)
  • 통합된 DevOps 도구체인
# 플랫폼 특화 기능 활용
# GitHub CLI 도구
gh repo create biolab/new-project --public
gh issue create --title "Memory optimization needed"
gh pr create --title "Add quality control pipeline"

# GitLab CLI 도구
glab repo create biolab/new-project --public
glab issue create --title "Performance bottleneck in alignment"
glab mr create --title "Implement parallel processing"

Fork와 Upstream 모델

Fork 모델은 오픈소스 프로젝트 기여와 안전한 실험을 위한 표준 워크플로우입니다. 생물정보학 도구 개발이나 기존 도구 개선에 널리 사용됩니다.

# Fork 기반 워크플로우
# 1. GitHub에서 원본 저장소 Fork (웹 인터페이스)

# 2. Fork된 저장소 복제
git clone git@github.com:your-username/bioinformatics-tool.git
cd bioinformatics-tool

# 3. 원본 저장소를 upstream으로 추가
git remote add upstream git@github.com:original-author/bioinformatics-tool.git
git remote -v
# origin    git@github.com:your-username/bioinformatics-tool.git (fetch)
# origin    git@github.com:your-username/bioinformatics-tool.git (push)
# upstream  git@github.com:original-author/bioinformatics-tool.git (fetch)
# upstream  git@github.com:original-author/bioinformatics-tool.git (push)

# 4. 기능 브랜치에서 작업
git checkout -b feature/optimize-memory-usage
# ... 코드 수정 작업 ...
git add -A
git commit -m "feat: Optimize memory usage in sequence alignment"

# 5. Fork된 저장소로 푸시
git push origin feature/optimize-memory-usage

# 6. Pull Request 생성 (GitHub 웹 인터페이스에서)

Upstream 동기화:

# 원본 저장소의 최신 변경사항 가져오기
git fetch upstream
git checkout main
git merge upstream/main

# 또는 rebase로 선형 이력 유지
git rebase upstream/main

# Fork 업데이트
git push origin main

# 기능 브랜치를 최신 main 기준으로 업데이트
git checkout feature/optimize-memory-usage
git rebase main
git push --force-with-lease origin feature/optimize-memory-usage

Pull Request / Merge Request 워크플로우

Pull Request(GitHub)나 Merge Request(GitLab)는 코드 변경사항을 검토하고 통합하는 핵심 메커니즘입니다. 코드 품질 보장과 지식 공유의 중요한 도구입니다.

Pull Request 생성과 관리:

# PR 생성 전 체크리스트
git status                          # 깔끔한 작업 디렉토리 확인
git log --oneline origin/main..HEAD # 포함될 커밋들 확인
git push origin feature-branch      # 최신 버전 푸시

# GitHub CLI로 PR 생성
gh pr create \
  --title "feat(alignment): Implement BWA-MEM2 integration" \
  --body "
## Description
Integrates BWA-MEM2 aligner as an alternative to BWA-MEM for improved performance on large datasets.

## Changes
- Add BWA-MEM2 wrapper in aligners/bwa_mem2.py
- Update pipeline configuration to support multiple aligners
- Add performance benchmarks

## Testing
- [x] Unit tests pass
- [x] Integration tests with sample data
- [x] Performance comparison with BWA-MEM

## Performance Impact
- 30% faster alignment on datasets >100GB
- Memory usage reduced by 15%

Fixes #123
" \
  --assignee @biolab-team \
  --label enhancement,performance

코드 리뷰 프로세스:

# 리뷰어가 PR 확인
gh pr checkout 42                   # PR #42를 로컬로 가져오기
git log --oneline main..            # 변경된 커밋들 확인
git diff main                       # 전체 변경사항 확인

# 특정 파일만 검토
git diff main -- aligners/bwa_mem2.py
git show HEAD                       # 최근 커밋 상세 확인

# 로컬에서 테스트
python -m pytest tests/test_aligners.py
python scripts/benchmark_alignment.py

# 리뷰 의견 제출 (GitHub 웹에서 또는 CLI로)
gh pr review 42 --approve -b "LGTM! Great performance improvements."
gh pr review 42 --request-changes -b "Please add error handling for invalid reference files."

리뷰 요청 사항 반영:

# 리뷰 피드백 반영
git checkout feature-branch
# ... 코드 수정 ...
git add -A
git commit -m "fix: Add error handling for invalid reference files"
git push origin feature-branch      # PR 자동 업데이트

# 대화형 리베이스로 커밋 정리 (필요시)
git rebase -i HEAD~3                # 최근 3개 커밋을 하나로 압축
git push --force-with-lease origin feature-branch

이슈 관리와 프로젝트 계획

이슈는 버그 보고, 기능 요청, 작업 계획 등을 체계적으로 관리하는 도구입니다. 생물정보학 프로젝트에서는 분석 요구사항, 성능 문제, 데이터 관련 이슈 등을 추적합니다.

# 이슈 템플릿 생성 (.github/ISSUE_TEMPLATE/bug_report.md)
cat > .github/ISSUE_TEMPLATE/bug_report.md << 'EOF'
---
name: Bug report
about: Create a report to help us improve
title: '[BUG] '
labels: bug
assignees: ''
---

## Describe the bug
A clear and concise description of what the bug is.

## To Reproduce
Steps to reproduce the behavior:
1. Input data format:
2. Command used:
3. Error message:

## Expected behavior
A clear and concise description of what you expected to happen.

## Environment
- OS: [e.g. Ubuntu 20.04]
- Python version: [e.g. 3.8.10]
- Tool version: [e.g. 2.1.0]
- Input data size: [e.g. 100GB FASTQ files]

## Additional context
Add any other context about the problem here.
EOF

# 이슈 생성
gh issue create \
  --title "[BUG] Memory overflow in large FASTQ processing" \
  --body "Processing FASTQ files >50GB causes memory overflow..." \
  --label bug,high-priority \
  --assignee @dev-team

프로젝트 보드 활용:

# GitHub Projects를 통한 칸반 보드 관리
# 열 구성: Backlog, In Progress, Review, Done

# 이슈를 프로젝트에 연결
gh issue create \
  --title "Implement STAR aligner support" \
  --project "RNA-seq Pipeline v2.0"

# 마일스톤 설정
gh issue create \
  --title "Add quality control metrics" \
  --milestone "v2.1 Release"

이슈와 커밋 연결:

# 커밋 메시지에서 이슈 참조
git commit -m "feat(qc): Add FastQC integration

Implements quality control using FastQC for read assessment.
Includes batch processing support for multiple samples.

Closes #45
Refs #23, #67"

# 이슈 자동 닫기 키워드들:
# Closes, Fixes, Resolves + 이슈 번호

코드 리뷰 모범 사례

효과적인 코드 리뷰는 코드 품질 향상, 지식 공유, 버그 조기 발견의 핵심입니다. 생물정보학에서는 알고리즘 정확성과 성능이 특히 중요합니다.

리뷰어 가이드라인:

# 체크해야 할 항목들
# 1. 코드 정확성
- 알고리즘 로직이 올바른가?
- 에러 처리가 적절한가?
- 경계값 처리가 안전한가?

# 2. 성능 고려사항
- 대용량 데이터에서 메모리 효율적인가?
- 시간 복잡도가 적절한가?
- 병렬 처리 가능성은?

# 3. 재현성과 안정성
- 같은 입력에 항상 같은 결과를 내는가?
- 의존성이 명확히 문서화되었는가?
- 테스트 케이스가 충분한가?

# 4. 문서화
- 함수/클래스 docstring이 있는가?
- 복잡한 알고리즘에 주석이 있는가?
- README나 사용법이 업데이트되었는가?

리뷰 의견 작성 패턴:

# 건설적 피드백 예시

# 개선 제안
"Consider using numpy.memmap for large array processing to reduce memory usage."

# 질문을 통한 개선
"What happens if the input FASTA file is empty? Should we add a check?"

# 대안 제시
"Instead of loading the entire file into memory, consider using a streaming approach with Bio.SeqIO.parse()."

# 칭찬과 학습
"Great use of argparse for command-line interface! This makes the tool much more user-friendly."

# 명확한 액션 아이템
"Please add a unit test for the edge case where sequence length is zero."

릴리스 관리

체계적인 릴리스 관리는 사용자에게 안정적인 버전을 제공하고 변경사항을 명확히 전달하는 데 필수적입니다.

# 시맨틱 버저닝 (Semantic Versioning)
# MAJOR.MINOR.PATCH (예: 2.1.3)
# MAJOR: 호환성을 깨는 변경
# MINOR: 하위 호환되는 기능 추가
# PATCH: 하위 호환되는 버그 수정

# 릴리스 브랜치 생성
git checkout develop
git checkout -b release/v2.1.0

# 버전 정보 업데이트
echo "VERSION = '2.1.0'" > src/version.py
git add src/version.py
git commit -m "chore: Bump version to 2.1.0"

# 릴리스 준비 (버그 수정, 문서 업데이트)
# ... 최종 조정 작업 ...

# 릴리스 완료
git checkout main
git merge --no-ff release/v2.1.0
git tag -a v2.1.0 -m "Release version 2.1.0

Features:
- Add STAR aligner support
- Improve memory efficiency by 30%
- Add batch processing capabilities

Bug fixes:
- Fix alignment parameter validation
- Resolve memory leak in large file processing

Breaking changes:
- Configuration file format updated (see migration guide)
"

git checkout develop
git merge --no-ff release/v2.1.0
git branch -d release/v2.1.0

GitHub Releases 생성:

# GitHub CLI로 릴리스 생성
gh release create v2.1.0 \
  --title "RNA-seq Pipeline v2.1.0" \
  --notes-file CHANGELOG.md \
  --target main

# 릴리스에 파일 첨부
gh release upload v2.1.0 dist/rna-seq-pipeline-2.1.0.tar.gz

# 사전 릴리스 (베타 버전)
gh release create v2.1.0-beta1 \
  --title "RNA-seq Pipeline v2.1.0 Beta 1" \
  --prerelease \
  --notes "Beta release for testing new STAR integration"

CHANGELOG.md 작성:

# Changelog

All notable changes to this project will be documented in this file.

## [2.1.0] - 2024-01-15

### Added

- STAR aligner integration for RNA-seq alignment
- Batch processing support for multiple samples
- Memory optimization options for large datasets
- Performance benchmarking tools

### Changed

- Configuration file format updated for better flexibility
- Default alignment parameters optimized for accuracy
- Improved error messages with suggested solutions

### Fixed

- Memory leak in large FASTQ file processing
- Incorrect quality score calculations in edge cases
- Thread safety issues in parallel processing

### Breaking Changes

- Configuration file format changed (see migration guide)
- Command-line argument `--threads` renamed to `--cpu-threads`

## [2.0.1] - 2023-12-20

### Fixed

- Critical bug in quality control step
- Documentation typos and missing examples

Practice Section: 원격 협업 실습

실습 1: GitHub 저장소 연결

cd ~/bioproject

# 원격 저장소 추가 (실제 URL로 대체 필요)
git remote add origin https://github.com/username/bioproject.git
git remote -v

# 첫 푸시
git branch -M main
git push -u origin main

echo "Repository connected to GitHub"

실습 2: 이슈 시뮬레이션

# 로컬에서 이슈 관련 작업 시뮬레이션
git checkout -b issue-23-add-logging

# 로깅 기능 추가
echo "import logging" > scripts/logger.py
echo "logging.basicConfig(level=logging.INFO)" >> scripts/logger.py

git add scripts/logger.py
git commit -m "feat: Add basic logging functionality

Implements logging infrastructure for better debugging.
Includes configurable log levels and file output.

Closes #23"

git checkout main

실습 3: Pull Request 시뮬레이션

# 기능 브랜치 작업
git checkout -b feature-data-validation

# 데이터 검증 기능 추가
echo "def validate_fastq(file): return True" > scripts/validator.py
git add scripts/validator.py
git commit -m "feat: Add FASTQ file validation"

# 추가 개선
echo "def validate_fasta(file): return True" >> scripts/validator.py
git add scripts/validator.py
git commit -m "feat: Add FASTA file validation"

# PR 준비 (실제로는 푸시 후 GitHub에서 생성)
git log --oneline main..HEAD
echo "Ready for Pull Request"

실습 4: 코드 리뷰 시뮬레이션

# 리뷰어 관점에서 변경사항 확인
git checkout feature-data-validation
git diff main

# 특정 파일 상세 확인
git show HEAD -- scripts/validator.py

# 리뷰 피드백 반영
echo "# Add docstring and error handling" >> scripts/validator.py
echo "def validate_fastq(file):" >> scripts/validator.py
echo '    """Validate FASTQ file format."""' >> scripts/validator.py
echo "    if not file: raise ValueError('File required')" >> scripts/validator.py
echo "    return True" >> scripts/validator.py

git add scripts/validator.py
git commit -m "docs: Add docstring and improve error handling"

실습 5: 릴리스 태그 생성

# 메인 브랜치로 전환
git checkout main

# 버전 태그 생성
git tag -a v1.0.0 -m "Release version 1.0.0

Initial release of bioinformatics analysis pipeline.

Features:
- Basic FASTQ/FASTA processing
- Quality control validation
- Logging infrastructure
"

# 태그 확인
git tag -l
git show v1.0.0

echo "Release v1.0.0 tagged"

핵심 정리

원격 협업 필수 명령어

# 원격 저장소 관리
git remote add origin <url>       # 원격 저장소 추가
git fetch origin                  # 원격 변경사항 가져오기
git pull origin main             # 가져와서 병합
git push origin branch           # 브랜치 푸시

# Fork 워크플로우
git remote add upstream <url>    # 원본 저장소 추가
git fetch upstream              # 원본 동기화
git rebase upstream/main        # 최신 상태로 업데이트

협업 모범 사례

# Pull Request 워크플로우
1. git checkout -b feature-branch    # 기능 브랜치 생성
2. # 작업 및 커밋
3. git push origin feature-branch   # 푸시
4. # GitHub에서 PR 생성
5. # 코드 리뷰 및 수정
6. # 승인 후 merge

# 커밋 메시지 패턴
git commit -m "type(scope): description

- Detailed explanation
- Why this change was needed
- What impact it has

Closes #123"

다음: 3.5 Advanced Git and Troubleshooting

3.5 Advanced Git and Troubleshooting

핵심 개념

고급 Git 기능과 문제 해결 기법은 복잡한 생물정보학 프로젝트에서 발생하는 다양한 상황을 효과적으로 관리하는 데 필수적입니다. 대용량 데이터셋, 장기간 실행되는 분석, 복잡한 협업 환경에서 Git을 안정적으로 활용하기 위한 고급 기법들을 다룹니다.

Git Stash: 작업 임시 저장

Stash는 현재 작업 중인 변경사항을 임시로 저장하여 브랜치를 깔끔하게 전환하거나 긴급한 작업을 수행할 수 있게 합니다. 생물정보학 연구에서는 장시간 실행되는 분석 중 긴급한 버그 수정이나 매개변수 조정이 필요할 때 유용합니다.

# 기본 stash 사용법
git stash                           # 현재 변경사항을 stash에 저장
git stash push -m "WIP: optimize memory usage in alignment"  # 메시지와 함께 저장
git stash list                      # stash 목록 확인
git stash show                      # 최신 stash 내용 요약
git stash show -p                   # 상세한 diff 확인

# stash 복원
git stash pop                       # 최신 stash를 적용하고 삭제
git stash apply                     # stash를 적용하지만 삭제하지 않음
git stash apply stash@{2}          # 특정 stash 적용
git stash drop stash@{1}           # 특정 stash 삭제
git stash clear                    # 모든 stash 삭제

고급 stash 활용:

# 선택적 stash
git stash push --keep-index         # 스테이징된 파일은 유지하고 나머지만 stash
git stash push --include-untracked  # 추적되지 않는 파일도 포함
git stash push -p                   # 대화형으로 일부분만 stash

# 특정 파일만 stash
git stash push -m "Save config changes" config/pipeline.yaml
git stash push -- "*.log"          # 패턴 매칭으로 특정 파일들만

# stash 브랜치 생성
git stash branch feature-memory-fix stash@{0}  # stash로부터 새 브랜치 생성

# 실제 사용 시나리오
# 상황: RNA-seq 분석 매개변수를 조정 중인데 긴급한 버그 수정이 필요
git stash push -m "WIP: RNA-seq parameter optimization"
git checkout main
git checkout -b hotfix-alignment-bug
# ... 버그 수정 작업 ...
git checkout feature-rna-seq-params
git stash pop

Cherry-pick: 선택적 커밋 적용

Cherry-pick은 특정 커밋만을 선택적으로 다른 브랜치에 적용하는 기능입니다. 생물정보학에서는 실험 브랜치의 특정 개선사항만을 메인 브랜치에 백포트하거나, 버그 수정을 여러 브랜치에 적용할 때 유용합니다.

# 기본 cherry-pick
git cherry-pick abc1234             # 특정 커밋을 현재 브랜치에 적용
git cherry-pick abc1234..def5678    # 커밋 범위 적용 (abc1234는 제외)
git cherry-pick abc1234^..def5678   # 커밋 범위 적용 (abc1234 포함)

# 여러 커밋 cherry-pick
git cherry-pick abc1234 def5678 ghi9012

# cherry-pick 옵션들
git cherry-pick -n abc1234          # 커밋하지 않고 변경사항만 적용
git cherry-pick -x abc1234          # 원본 커밋 정보를 메시지에 추가
git cherry-pick --no-commit abc1234 # 자동 커밋 비활성화

# 충돌 해결
git cherry-pick abc1234
# 충돌 발생 시
git status                          # 충돌 파일 확인
# ... 충돌 해결 ...
git add resolved-file.py
git cherry-pick --continue          # cherry-pick 계속
git cherry-pick --abort             # cherry-pick 취소

실제 사용 사례:

# 시나리오: 실험 브랜치의 성능 최적화를 메인 브랜치에 적용
git log --oneline experiment/deep-learning
# a1b2c3d feat: optimize GPU memory usage
# e4f5g6h feat: add neural network layer
# i7j8k9l fix: memory leak in training loop

git checkout main
git cherry-pick a1b2c3d             # GPU 메모리 최적화만 적용
git cherry-pick i7j8k9l             # 메모리 누수 수정도 적용
# 신경망 레이어는 실험적이므로 제외

# 여러 브랜치에 버그 수정 적용
bug_fix_commit="m1n2o3p"
for branch in release/v2.0 release/v2.1 develop; do
    git checkout $branch
    git cherry-pick $bug_fix_commit
done

Git Rebase 고급 활용

Rebase는 커밋 이력을 정리하고 선형적으로 만드는 강력한 도구입니다. 대화형 rebase를 통해 커밋 메시지 수정, 커밋 분할/병합, 순서 변경 등이 가능합니다.

# 대화형 rebase
git rebase -i HEAD~5                # 최근 5개 커밋 편집
git rebase -i main                  # main 이후의 모든 커밋 편집

# rebase 편집 옵션들:
# pick (p): 커밋 그대로 사용
# reword (r): 커밋 메시지 수정
# edit (e): 커밋 수정 (파일 변경 가능)
# squash (s): 이전 커밋과 합치고 메시지 수정
# fixup (f): 이전 커밋과 합치고 메시지 버리기
# drop (d): 커밋 삭제

대화형 rebase 예시:

# 편집기에서 표시되는 내용
pick a1b2c3d feat: add quality control module
pick e4f5g6h fix: typo in variable name
pick i7j8k9l feat: improve memory efficiency
pick m1n2o3p fix: handle empty input files
pick q5r6s7t docs: update README with examples

# 편집 후:
pick a1b2c3d feat: add quality control module
fixup e4f5g6h fix: typo in variable name
pick i7j8k9l feat: improve memory efficiency
squash m1n2o3p fix: handle empty input files
reword q5r6s7t docs: update README with examples

# 결과: 5개 커밋이 3개로 정리됨

커밋 분할과 수정:

# 특정 커밋을 여러 개로 분할
git rebase -i HEAD~3
# 편집기에서 분할하려는 커밋을 'edit'으로 변경

# rebase가 해당 커밋에서 멈춤
git reset HEAD~                     # 커밋 취소하고 변경사항을 작업 디렉토리로
git add scripts/qc.py              # 첫 번째 부분만 스테이징
git commit -m "feat: add quality control core functions"
git add config/qc_params.yaml     # 두 번째 부분 스테이징
git commit -m "config: add QC parameter defaults"
git rebase --continue              # rebase 계속

Git Hooks: 자동화와 정책 강제

Git hooks는 특정 Git 이벤트가 발생할 때 자동으로 실행되는 스크립트입니다. 생물정보학 프로젝트에서는 코드 품질 검사, 테스트 자동 실행, 데이터 검증 등에 활용할 수 있습니다.

# hooks 디렉토리 확인
ls -la .git/hooks/
# 기본적으로 .sample 파일들이 있음

# pre-commit hook 생성 (커밋 전 실행)
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# 생물정보학 프로젝트용 pre-commit hook

echo "Running pre-commit checks..."

# Python 코드 스타일 검사
if command -v black &> /dev/null; then
    echo "Checking Python code format..."
    black --check scripts/ || {
        echo "Code formatting issues found. Run 'black scripts/' to fix."
        exit 1
    }
fi

# 테스트 실행
if [ -d "tests" ]; then
    echo "Running tests..."
    python -m pytest tests/ || {
        echo "Tests failed. Please fix before committing."
        exit 1
    }
fi

# 대용량 파일 검사
echo "Checking for large files..."
for file in $(git diff --cached --name-only); do
    if [ -f "$file" ]; then
        size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
        if [ "$size" -gt 104857600 ]; then  # 100MB
            echo "Error: $file is larger than 100MB"
            echo "Large data files should not be committed to Git"
            exit 1
        fi
    fi
done

echo "Pre-commit checks passed!"
EOF

chmod +x .git/hooks/pre-commit

다른 유용한 hooks:

# commit-msg hook (커밋 메시지 검증)
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/bash
# 커밋 메시지 형식 검증

commit_regex='^(feat|fix|docs|style|refactor|test|chore|data|analysis)(\(.+\))?: .{1,50}'

if ! grep -qE "$commit_regex" "$1"; then
    echo "Invalid commit message format!"
    echo "Format: type(scope): description"
    echo "Example: feat(alignment): add BWA-MEM2 support"
    exit 1
fi
EOF

chmod +x .git/hooks/commit-msg

# pre-push hook (푸시 전 실행)
cat > .git/hooks/pre-push << 'EOF'
#!/bin/bash
# 푸시 전 통합 테스트 실행

echo "Running integration tests before push..."
if [ -f "run_integration_tests.sh" ]; then
    ./run_integration_tests.sh || {
        echo "Integration tests failed. Push aborted."
        exit 1
    }
fi
EOF

chmod +x .git/hooks/pre-push

대용량 파일과 성능 최적화

생물정보학 프로젝트는 종종 대용량 데이터 파일을 다룹니다. Git은 기본적으로 이런 파일에 최적화되어 있지 않으므로 특별한 관리가 필요합니다.

# Git LFS (Large File Storage) 설정
git lfs install                     # LFS 활성화
git lfs track "*.bam"               # BAM 파일을 LFS로 관리
git lfs track "*.fastq.gz"          # 압축된 FASTQ 파일
git lfs track "data/large/*"        # 특정 디렉토리

# .gitattributes 파일 자동 생성 확인
cat .gitattributes
# *.bam filter=lfs diff=lfs merge=lfs -text
# *.fastq.gz filter=lfs diff=lfs merge=lfs -text

# LFS 파일 상태 확인
git lfs ls-files                    # LFS로 관리되는 파일 목록
git lfs status                      # LFS 파일 상태

# 기존 대용량 파일을 LFS로 마이그레이션
git lfs migrate import --include="*.bam" --everything

저장소 최적화:

# 저장소 크기 확인
git count-objects -vH
# count 2847
# size 45.67 MiB
# in-pack 28234
# packs 1
# size-pack 123.45 MiB

# 가비지 컬렉션
git gc                              # 표준 정리
git gc --aggressive                 # 더 철저한 정리 (시간이 오래 걸림)

# 참조되지 않는 객체 제거
git prune                           # 2주 이상 된 객체 제거
git prune --expire=1.week          # 1주 이상 된 객체 제거

# 저장소 압축 최적화
git repack -ad                      # 모든 객체를 하나의 팩 파일로 압축

# 히스토리에서 완전히 파일 제거 (위험!)
git filter-branch --tree-filter 'rm -f large_data.bam' HEAD
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git gc --prune=now

문제 해결과 복구

Git 사용 중 발생할 수 있는 다양한 문제 상황과 해결 방법을 다룹니다.

커밋 복구:

# 실수로 삭제한 커밋 복구
git reflog                          # 최근 HEAD 이동 이력 확인
git show HEAD@{5}                   # 5단계 전 상태 확인
git reset --hard HEAD@{5}           # 해당 상태로 복구

# 삭제된 브랜치 복구
git reflog --all | grep branch-name
git checkout -b recovered-branch commit-hash

# 특정 파일의 이전 버전 복구
git checkout HEAD~3 -- important-script.py    # 3커밋 전 버전으로 복구
git checkout branch-name -- missing-file.py   # 다른 브랜치에서 파일 가져오기

머지 충돌 고급 해결:

# 복잡한 충돌 상황에서 전략 선택
git merge -X ours feature-branch               # 충돌 시 현재 브랜치 우선
git merge -X theirs feature-branch             # 충돌 시 머지 브랜치 우선

# 특정 파일만 선택적으로 해결
git checkout --ours scripts/config.py         # 현재 브랜치 버전 선택
git checkout --theirs scripts/analysis.py     # 머지 브랜치 버전 선택
git add scripts/config.py scripts/analysis.py
git commit

# 3-way diff로 충돌 해결
git mergetool --tool=vimdiff                   # 시각적 도구 사용

corrupted 저장소 복구:

# 저장소 무결성 검사
git fsck --full                     # 전체 무결성 검사
git fsck --unreachable             # 참조되지 않는 객체 확인

# 객체 손상 시 복구
git hash-object -w file.txt         # 파일에서 객체 재생성
git update-index --add file.txt     # 인덱스 업데이트

# 백업에서 복구
git remote add backup /path/to/backup/repo
git fetch backup
git reset --hard backup/main       # 백업으로 완전 복구

성능 문제 진단:

# 느린 명령어 디버깅
git config --global core.preloadindex true    # 인덱스 미리 로드
git config --global core.fscache true         # 파일시스템 캐시 활성화
git config --global gc.auto 256               # 자동 GC 빈도 조정

# 큰 파일 찾기
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  sed -n 's/^blob //p' | \
  sort --numeric-sort --key=2 | \
  tail -10                          # 가장 큰 10개 파일

# 히스토리 분석
git log --stat --summary | grep -E "^ (create|delete) mode" | wc -l    # 생성/삭제된 파일 수
git shortlog -sn                                                        # 기여자별 커밋 수

Practice Section: 고급 Git 기능 실습

실습 1: Stash 활용

cd ~/bioproject

# 작업 중인 변경사항 생성
echo "# TODO: implement advanced QC" >> scripts/qc.py
echo "memory_limit = '64GB'" >> config/settings.yaml

# Stash로 임시 저장
git stash push -m "WIP: QC improvements and memory config"
git status  # 깨끗한 상태 확인

# 다른 작업 수행
echo "def emergency_fix(): pass" > scripts/hotfix.py
git add scripts/hotfix.py
git commit -m "hotfix: add emergency repair function"

# 이전 작업 복원
git stash list
git stash pop
git status

실습 2: Cherry-pick 연습

# 실험 브랜치 생성
git checkout -b experiment/optimization
echo "# Performance optimization" > scripts/optimize.py
git add scripts/optimize.py
git commit -m "feat: add performance optimization"

echo "def fast_align(): pass" >> scripts/optimize.py
git add scripts/optimize.py
git commit -m "feat: implement fast alignment algorithm"

# 메인으로 돌아가서 첫 번째 커밋만 적용
git checkout main
COMMIT_HASH=$(git log experiment/optimization --oneline | tail -1 | cut -d' ' -f1)
git cherry-pick $COMMIT_HASH

git log --oneline -3

실습 3: 대화형 Rebase

# 여러 커밋 생성
git checkout -b feature/cleanup
echo "import sys" > scripts/utils.py
git add scripts/utils.py
git commit -m "add utils module"

echo "import os" >> scripts/utils.py
git add scripts/utils.py
git commit -m "add os import"

echo "def helper(): pass" >> scripts/utils.py
git add scripts/utils.py
git commit -m "add helper function"

# 커밋 정리 (실제로는 편집기가 열림)
echo "Commits created for rebase practice"
git log --oneline -3

실습 4: Git Hooks 설정

# 간단한 pre-commit hook 생성
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
echo "Running pre-commit checks..."

# Python 파일 기본 문법 검사
for file in $(git diff --cached --name-only | grep '\.py$'); do
    if [ -f "$file" ]; then
        python -m py_compile "$file" || {
            echo "Syntax error in $file"
            exit 1
        }
    fi
done

echo "Pre-commit checks passed!"
EOF

chmod +x .git/hooks/pre-commit

# Hook 테스트
echo "invalid python syntax!" > scripts/bad_syntax.py
git add scripts/bad_syntax.py
# git commit -m "test hook" # 이 커밋은 실패해야 함
git reset HEAD scripts/bad_syntax.py
rm scripts/bad_syntax.py

실습 5: 문제 해결 시뮬레이션

# 커밋 실수 시뮬레이션
echo "sensitive_data = 'password123'" > scripts/secret.py
git add scripts/secret.py
git commit -m "accidentally commit secret"

# 방금 커밋 수정
git reset --soft HEAD~1
rm scripts/secret.py
echo "secret.py" >> .gitignore
git add .gitignore
git commit -m "fix: add .gitignore for sensitive files"

# reflog로 이력 확인
git reflog | head -5
echo "Problem resolved using git reset"

핵심 정리

고급 Git 명령어

# Stash 관리
git stash push -m "message"        # 작업 임시 저장
git stash pop                      # 최신 stash 적용 및 삭제
git stash list                     # stash 목록 확인

# 선택적 적용
git cherry-pick <commit>           # 특정 커밋 적용
git rebase -i HEAD~n              # 대화형 리베이스

# 문제 해결
git reflog                         # HEAD 이동 이력
git fsck --full                   # 저장소 무결성 검사
git reset --hard HEAD@{n}         # 특정 시점으로 복구

생물정보학 프로젝트 최적화

# 대용량 파일 관리
git lfs track "*.bam"             # LFS로 큰 파일 관리
git gc --aggressive               # 저장소 최적화

# Hooks 활용
.git/hooks/pre-commit             # 커밋 전 검증
.git/hooks/pre-push              # 푸시 전 테스트

# 성능 튜닝
git config core.preloadindex true # 인덱스 미리 로드
git config gc.auto 256           # 자동 GC 조정

Chapter 3 완료: Git을 통한 버전 관리와 협업의 모든 측면을 다뤘습니다. 다음 장에서는 암호학과 보안의 기초를 학습하겠습니다.

다음: Chapter 4: Cryptography and Security Fundamentals

Chapter 4: Security & Cryptography

4.1 Cryptography Fundamentals

4.2 SSH Key Management

4.3 GPG Data Encryption

4.4 Age Encryption & Secret Management

4.5 Research Data Security

Chapter 5: Python for Science

5.1 Python & Scientific Ecosystem

5.2 NumPy & Array Operations

5.3 Pandas & Data Manipulation

5.4 Matplotlib & Seaborn Visualization

5.5 Jupyter & Reproducible Notebooks

Chapter 6: Data Science Fundamentals

6.1 Exploratory Data Analysis

6.2 Statistical Hypothesis Testing

6.3 Data Preprocessing & Cleaning

6.4 Dimensionality Reduction & Clustering

6.5 Insight Generation through Visualization

Chapter 7: Machine Learning Basics

7.1 Supervised Learning Algorithms

7.2 Unsupervised Learning & Pattern Discovery

7.3 Model Evaluation & Validation

7.4 Hyperparameter Tuning

7.5 Scikit-learn Practice

Biological Data Formats

8.1 Sequence Data Formats

8.2 Structure Data Formats

8.3 Annotation Data Formats

8.4 Experimental Data Integration

Sequence Analysis

9.1 BioPython Basics

9.2 Sequence Alignment Algorithms

9.3 BLAST & Database Searching

9.4 Multiple Sequence Alignment

9.5 Sequence Motif Discovery

Genomics & Transcriptomics

10.1 Genome Assembly & Annotation

10.2 RNA-seq Data Analysis Pipeline

10.3 Differential Gene Expression

10.4 Functional Enrichment Analysis

10.5 Single-cell RNA-seq Basics

Synthetic Biology Tools

11.1 Genetic Circuit Design Tools

11.2 DNA Assembly Strategies

11.3 Codon Optimization & Expression Prediction

11.4 Metabolic Pathway Analysis

11.5 SBOL Standards & Design Automation

Evolutionary Engineering

12.1 Evolution Simulation & Modeling

12.2 Adaptive Laboratory Evolution

12.3 Genetic Diversity Analysis

12.4 Selection Pressure Quantification

12.5 Evolutionary Trajectory Prediction

Research Workflows

13.1 Research Project Structure

13.2 Data Management Plans

13.3 Reproducible Analysis Pipelines

13.4 Documentation & Reporting

High-Performance Computing

14.1 Parallel Processing

14.2 Cluster Computing

14.3 Container-based Workflows

14.4 Cloud Computing

CI/CD for Research

15.1 Research Code Testing

15.2 Data Pipeline Automation

15.3 Reproducibility Validation

15.4 Research Environment Deployment

Appendix A. Nix Flakes: 완전한 재현성 보장

A.1 기존 방식의 한계

shell.nix의 재현성 문제

# 목적: 기존 방식의 문제점 파악
# 문제점: nixpkgs 버전 미고정
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = with pkgs; [
    python38
    python38Packages.pandas
  ];
}

문제: <nixpkgs>가 시스템 채널에 의존하여 시점에 따라 다른 환경 생성

# 2023년 10월 실행
$ nix-shell basic-env.nix
Python 3.8.16, pandas 1.5.3

# 2024년 3월 실행 (nixpkgs 업데이트 후)
$ nix-shell basic-env.nix
Python 3.8.18, pandas 2.0.1  # 다른 버전

A.2 Flakes 개념

정의와 구조

Flake는 프로젝트의 모든 의존성을 명시적으로 선언하고 버전을 정확히 고정하는 시스템입니다.

핵심 파일:

  • flake.nix: 의존성과 출력 정의
  • flake.lock: 정확한 버전 해시 (자동 생성)

기본 구조

# 목적: Flake 기본 구조 이해
# 핵심 개념: inputs, outputs, 버전 고정
{
  description = "개발 환경 프로젝트";

  # 모든 외부 의존성 명시
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
  };

  # 제공할 출력들 정의
  outputs = { self, nixpkgs }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
    in {
      devShells.${system}.default = pkgs.mkShell {
        buildInputs = with pkgs; [
          python38
          python38Packages.pandas
        ];
      };
    };
}

A.3 다중 환경 Flake

환경별 구성

# 목적: 프로젝트 단계별 환경 제공
# 핵심 개념: 다중 출력, 조합성
{
  description = "데이터 분석 프로젝트";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
  };

  outputs = { nixpkgs, ... }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};

      mkPythonEnv = extraPackages: pkgs.mkShell {
        buildInputs = with pkgs; [
          python38
          python38Packages.pandas
          python38Packages.numpy
        ] ++ extraPackages;
      };
    in {
      devShells.${system} = {
        # 기본 분석 환경
        default = mkPythonEnv (with pkgs; [
          python38Packages.matplotlib
        ]);

        # 기계학습 환경
        ml = mkPythonEnv (with pkgs; [
          python38Packages.tensorflow
          python38Packages.scikit-learn
        ]);

        # 웹 개발 환경
        web = mkPythonEnv (with pkgs; [
          python38Packages.flask
          python38Packages.requests
        ]);
      };
    };
}

사용법

# 기본 환경
$ nix develop

# 특정 환경
$ nix develop .#ml
$ nix develop .#web

A.4 실제 프로젝트 구성

프로젝트 구조

data-analysis/
├── flake.nix
├── flake.lock
├── src/
│   ├── analysis.py
│   └── utils.py
├── data/
│   └── dataset.csv
└── README.md

완전한 프로젝트 Flake

# 목적: 실무 프로젝트 설정
# 핵심 개념: 개발도구 통합, 스크립트 제공
{
  description = "데이터 분석 프로젝트";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
  };

  outputs = { nixpkgs, ... }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
    in {
      devShells.${system}.default = pkgs.mkShell {
        buildInputs = with pkgs; [
          # Python 환경
          python38
          python38Packages.pandas
          python38Packages.matplotlib
          python38Packages.jupyter

          # 개발 도구
          git
          black  # 코드 포매터
        ];

        shellHook = ''
          echo "데이터 분석 환경"
          echo "데이터: $(ls data/ | wc -l) 파일"
          echo ""
          echo "사용법:"
          echo "  python src/analysis.py"
          echo "  jupyter notebook"
        '';
      };

      # 분석 스크립트를 앱으로 제공
      apps.${system}.analysis = {
        type = "app";
        program = "${pkgs.python38}/bin/python";
        args = [ "src/analysis.py" ];
      };
    };
}

A.5 버전 관리

flake.lock 파일

{
  "nodes": {
    "nixpkgs": {
      "locked": {
        "lastModified": 1697723726,
        "narHash": "sha256-SaTWPkI8a5xSHX/rrKzUe9...",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "7816540796e4f6b4f1a1e8c45be6c54f9e1b8db7",
        "type": "github"
      }
    }
  }
}

의미: 정확한 커밋 해시로 nixpkgs 버전 고정하여 재현성 보장

업데이트 관리

# 현재 버전 확인
$ nix flake metadata

# 의존성 업데이트
$ nix flake update

# 특정 입력만 업데이트
$ nix flake update nixpkgs

A.6 팀 협업

Git 통합

# 프로젝트 초기화
$ git init
$ git add flake.nix flake.lock src/ data/
$ git commit -m "프로젝트 초기 설정"

팀원 환경 구성

# 저장소 복제
$ git clone https://github.com/team/data-project.git
$ cd data-project

# 환경 즉시 사용
$ nix develop  # flake.lock 기반으로 동일한 환경 구성

A.7 기존 프로젝트 마이그레이션

shell.nix → flake.nix 변환

기존:

{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  buildInputs = with pkgs; [ python38 git ];
}

변환 후:

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
  outputs = { nixpkgs, ... }: {
    devShells.x86_64-linux.default =
      nixpkgs.legacyPackages.x86_64-linux.mkShell {
        buildInputs = with nixpkgs.legacyPackages.x86_64-linux; [
          python38 git
        ];
      };
  };
}

요약: Flakes는 모든 의존성을 명시적으로 관리하여 완전한 재현성을 보장합니다. 프로젝트와 환경을 하나의 단위로 관리하여 팀 협업과 장기 보존에 필수적입니다.

Appendix B: NixOS 기초

B.1 NixOS 개념

선언적 시스템 관리

NixOS는 전체 Linux 시스템을 Nix 언어로 선언적으로 관리하는 배포판입니다.

# 목적: 시스템 전체를 코드로 정의
# 핵심 개념: 선언적 구성, 불변 시스템
{
  # 사용자 정의
  users.users.developer = {
    isNormalUser = true;
    extraGroups = [ "wheel" "docker" ];
  };

  # 서비스 활성화
  services.openssh.enable = true;
  services.docker.enable = true;

  # 시스템 패키지
  environment.systemPackages = with pkgs; [
    git vim curl python3
  ];
}

핵심 특징:

  • 시스템 상태를 코드로 버전 관리
  • 원자적 업데이트와 롤백
  • 완전한 재현성

B.2 설치 과정

최소 요구사항

  • RAM: 4GB 이상
  • 디스크: 20GB 이상
  • 아키텍처: x86_64, aarch64

기본 설치 절차

# NixOS ISO 부팅 후 파티션 생성
parted /dev/sda -- mklabel gpt
parted /dev/sda -- mkpart primary 512MiB -8GiB
parted /dev/sda -- mkpart primary linux-swap -8GiB 100%
parted /dev/sda -- mkpart ESP fat32 1MiB 512MiB

# 파일시스템 생성
mkfs.ext4 -L nixos /dev/sda1
mkswap -L swap /dev/sda2
mkfs.fat -F 32 -n boot /dev/sda3

# 마운트 및 설치
mount /dev/disk/by-label/nixos /mnt
mkdir -p /mnt/boot
mount /dev/disk/by-label/boot /mnt/boot
swapon /dev/sda2

nixos-generate-config --root /mnt
nixos-install

B.3 시스템 구성

기본 서버 설정

# 목적: 개발 서버 기본 구성
# 핵심 개념: 서비스 정의, 사용자 관리
{ config, pkgs, ... }:

{
  imports = [ ./hardware-configuration.nix ];

  # 부트로더
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  # 네트워크
  networking = {
    hostName = "dev-server";
    networkmanager.enable = true;
    firewall = {
      enable = true;
      allowedTCPPorts = [ 22 80 443 ];
    };
  };

  # SSH 설정
  services.openssh = {
    enable = true;
    settings = {
      PasswordAuthentication = false;
      PermitRootLogin = "no";
    };
  };

  # 시스템 패키지
  environment.systemPackages = with pkgs; [
    git vim curl wget htop
    python3 nodejs docker
  ];

  # 가상화
  virtualisation.docker.enable = true;

  system.stateVersion = "23.11";
}

개발 환경 서버

# 목적: 개발팀용 서버 구성
# 핵심 개념: 다중 사용자, 개발 도구
{
  imports = [ ./base-config.nix ];

  # 사용자 계정
  users.users = {
    developer1 = {
      isNormalUser = true;
      extraGroups = [ "wheel" "docker" ];
      openssh.authorizedKeys.keys = [
        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... developer1@laptop"
      ];
    };

    developer2 = {
      isNormalUser = true;
      extraGroups = [ "wheel" "docker" ];
      openssh.authorizedKeys.keys = [
        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... developer2@laptop"
      ];
    };
  };

  # 개발 도구
  environment.systemPackages = with pkgs; [
    # 언어별 런타임
    python3 nodejs-18_x go rustc

    # 데이터베이스 클라이언트
    postgresql mysql80

    # 빌드 도구
    gnumake cmake gcc
  ];

  # 데이터베이스 서비스
  services.postgresql = {
    enable = true;
    ensureDatabases = [ "development" ];
  };
}

B.4 서비스 관리

웹 서버 구성

# 목적: Nginx 웹 서버 설정
# 핵심 개념: 리버스 프록시, SSL
{
  services.nginx = {
    enable = true;
    virtualHosts."api.example.com" = {
      forceSSL = true;
      enableACME = true;
      locations."/" = {
        proxyPass = "http://localhost:3000";
      };
    };
  };

  # 자동 SSL 인증서
  security.acme = {
    acceptTerms = true;
    defaults.email = "admin@example.com";
  };
}

백업 서비스

# 목적: 자동 백업 설정
# 핵심 개념: 스케줄링, 데이터 보호
{
  services.restic.backups.daily = {
    repository = "/backup/restic";
    paths = [ "/home" "/etc/nixos" "/var/lib" ];
    passwordFile = "/etc/nixos/backup-password";
    timerConfig = {
      OnCalendar = "daily";
      Persistent = true;
    };
  };
}

B.5 시스템 관리

업데이트와 롤백

# 시스템 업데이트
sudo nixos-rebuild switch

# 설정 테스트 (재부팅 없이)
sudo nixos-rebuild test

# 이전 설정으로 롤백
sudo nixos-rebuild switch --rollback

# 세대 목록 확인
sudo nix-env --list-generations --profile /nix/var/nix/profiles/system

원격 관리

# 원격 빌드 및 배포
nixos-rebuild switch --target-host user@server --use-remote-sudo

# 설정 파일 동기화
rsync -av ./nixos-config/ server:/etc/nixos/

B.6 실무 사례

CI/CD 서버

# 목적: 지속적 통합 서버 구성
# 핵심 개념: 자동화, 컨테이너 지원
{
  # GitLab Runner
  services.gitlab-runner = {
    enable = true;
    services.default = {
      registrationConfigFile = "/etc/nixos/gitlab-runner-config";
      executor = "docker";
    };
  };

  # Docker 레지스트리
  services.dockerRegistry = {
    enable = true;
    port = 5000;
  };

  # 모니터링
  services.prometheus = {
    enable = true;
    exporters.node.enable = true;
  };
}

데이터베이스 서버

# 목적: 고가용성 데이터베이스 구성
# 핵심 개념: 데이터 지속성, 백업
{
  services.postgresql = {
    enable = true;
    package = pkgs.postgresql_15;
    dataDir = "/var/lib/postgresql/15";

    settings = {
      max_connections = 200;
      shared_buffers = "256MB";
      effective_cache_size = "1GB";
    };

    ensureDatabases = [ "production" "staging" ];
    ensureUsers = [
      {
        name = "app";
        ensurePermissions = {
          "DATABASE production" = "ALL PRIVILEGES";
        };
      }
    ];
  };

  # 자동 백업
  services.postgresqlBackup = {
    enable = true;
    databases = [ "production" ];
    startAt = "02:00";
  };
}

B.7 특징과 제한사항

장점

특성설명
재현성설정 파일로 완전히 동일한 시스템 재구성
롤백이전 시스템 상태로 즉시 복원
원자성업데이트 성공 또는 완전 실패
테스트프로덕션 적용 전 안전한 테스트

제한사항

  • 전통적 Linux 관리 방식과 상이
  • 학습 곡선 존재
  • 일부 소프트웨어 호환성 이슈
  • FHS(Filesystem Hierarchy Standard) 비준수

적용 시점

권장:

  • 서버 인프라 관리
  • 개발팀 표준화
  • 시스템 설정 버전 관리

비권장:

  • 데스크톱 일반 사용
  • Linux 초보자
  • 레거시 시스템 의존성

요약: NixOS는 전체 시스템을 선언적으로 관리하는 Linux 배포판입니다. 서버 환경에서 재현성과 안정성을 보장하지만, 전통적 관리 방식과는 다른 접근법을 요구합니다.

Appendix C: nix-darwin 기초

C.1 nix-darwin 개념

macOS 시스템 관리

nix-darwin은 macOS 시스템 설정을 Nix 언어로 선언적으로 관리하는 도구입니다.

# 목적: macOS 시스템을 코드로 정의
# 핵심 개념: 선언적 설정, Homebrew 통합
{
  # 시스템 기본값
  system.defaults = {
    dock.autohide = true;
    finder.FXPreferredViewStyle = "clmv";
  };

  # Homebrew 패키지
  homebrew = {
    enable = true;
    brews = [ "git" "curl" ];
    casks = [ "visual-studio-code" "docker" ];
  };

  # Nix 패키지
  environment.systemPackages = with pkgs; [
    python3 nodejs
  ];
}

활용 목적:

  • 개발 환경 표준화
  • 새 Mac 설정 자동화
  • 팀 환경 일관성 확보

C.2 설치

전제 조건

# Nix 설치 확인
nix --version

# Xcode Command Line Tools 설치
xcode-select --install

Flake 기반 설치

# 목적: nix-darwin Flake 구성
# 핵심 개념: 시스템 설정 모듈화
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    nix-darwin = {
      url = "github:nix-darwin/nix-darwin";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { nix-darwin, nixpkgs, ... }: {
    darwinConfigurations."MacBook-Dev" = nix-darwin.lib.darwinSystem {
      modules = [ ./darwin-configuration.nix ];
    };
  };
}
# 설치 실행
nix run nix-darwin -- switch --flake ~/config#MacBook-Dev

C.3 개발 환경 구성

기본 개발자 설정

# 목적: 개발자용 macOS 설정
# 핵심 개념: 생산성 향상, 도구 통합
{ config, pkgs, ... }:

{
  # Nix 설정
  nix.settings = {
    experimental-features = [ "nix-command" "flakes" ];
    auto-optimise-store = true;
  };

  # 시스템 패키지
  environment.systemPackages = with pkgs; [
    git vim curl wget tree htop
    python3 nodejs docker-client
  ];

  # 시스템 기본값
  system.defaults = {
    dock = {
      autohide = true;
      orientation = "bottom";
      tilesize = 48;
    };

    finder = {
      AppleShowAllExtensions = true;
      FXPreferredViewStyle = "clmv";
      ShowStatusBar = true;
    };

    NSGlobalDomain = {
      AppleKeyboardUIMode = 3;
      KeyRepeat = 1;
      InitialKeyRepeat = 14;
    };
  };

  system.stateVersion = 4;
}

전문 개발 환경

# 목적: 특화된 개발 환경 구성
# 핵심 개념: 언어별 도구, 전문 소프트웨어
{
  imports = [ ./base-darwin.nix ];

  # 개발 도구
  environment.systemPackages = with pkgs; [
    # 언어 런타임
    python3 nodejs go rustc

    # 빌드 도구
    cmake gnumake

    # 데이터베이스 클라이언트
    postgresql mysql80
  ];

  # 개발 환경 변수
  environment.variables = {
    EDITOR = "vim";
    PYTHON_PATH = "${pkgs.python3}/bin/python";
  };
}

C.4 Homebrew 통합

패키지 관리 전략

# 목적: Nix와 Homebrew 역할 분담
# 핵심 개념: 도구별 최적 활용
{
  homebrew = {
    enable = true;

    # CLI 도구 (Nix가 더 적합한 경우)
    brews = [
      "postgresql@14"  # 특정 버전 필요
      "redis"
    ];

    # GUI 애플리케이션 (Homebrew가 적합)
    casks = [
      "visual-studio-code"
      "docker"
      "firefox"
      "slack"
    ];

    # 자동 정리
    onActivation = {
      cleanup = "zap";
      autoUpdate = true;
      upgrade = true;
    };
  };

  # Nix로 관리할 도구들
  environment.systemPackages = with pkgs; [
    git curl wget htop jq
    python3 nodejs
  ];
}

패키지 선택 기준

도구 유형Nix 사용Homebrew 사용
CLI 도구✅ git, curl, python
GUI 앱✅ VSCode, Docker
시스템 도구✅ 드라이버, 폰트
개발 런타임✅ 버전 관리 중요⚠️ 특정 버전만

C.5 시스템 설정 관리

프로그래밍 도구 설정

# 목적: 개발 도구 통합 설정
# 핵심 개념: 설정 파일 관리, 자동화
{
  # Git 전역 설정
  programs.git = {
    enable = true;
    config = {
      user.name = "Developer";
      user.email = "dev@company.com";
      init.defaultBranch = "main";
      pull.rebase = true;
    };
  };

  # 셸 설정
  programs.zsh = {
    enable = true;
    enableCompletion = true;
    enableAutosuggestions = true;
  };

  # 환경 변수
  environment = {
    variables = {
      EDITOR = "vim";
      LANG = "en_US.UTF-8";
    };

    shellAliases = {
      ll = "ls -la";
      rebuild = "darwin-rebuild switch --flake ~/config";
    };
  };
}

보안 설정

# 목적: 시스템 보안 강화
# 핵심 개념: 방화벽, 개인정보 보호
{
  # 방화벽
  system.defaults.alf = {
    globalstate = 1;
    allowsignedenabled = 1;
  };

  # 개인정보 보호
  system.defaults.NSGlobalDomain = {
    NSAutomaticCapitalizationEnabled = false;
    NSAutomaticSpellingCorrectionEnabled = false;
  };

  # 자동 업데이트 비활성화 (수동 제어)
  system.defaults.SoftwareUpdate.AutomaticallyInstallMacOSUpdates = false;
}

C.6 팀 표준화

공통 설정 모듈

# 목적: 팀 전체 표준 설정
# 핵심 개념: 설정 모듈화, 재사용
{ config, pkgs, ... }:

{
  # 공통 개발 도구
  environment.systemPackages = with pkgs; [
    git vim curl
    python3 nodejs docker-client
  ];

  # 공통 GUI 도구
  homebrew.casks = [
    "visual-studio-code"
    "docker"
    "slack"
  ];

  # 공통 시스템 설정
  system.defaults = {
    dock.tilesize = 48;
    finder.FXPreferredViewStyle = "clmv";
  };
}

개인 설정 확장

# 목적: 공통 설정 + 개인 커스터마이징
# 핵심 개념: 설정 상속, 개인화
{
  imports = [ ./team-common.nix ];

  # 개인 추가 도구
  environment.systemPackages = with pkgs; [
    tmux neovim
  ];

  homebrew.casks = [
    "spotify"
    "notion"
  ];
}

C.7 시스템 관리

설정 적용과 업데이트

# 설정 적용
darwin-rebuild switch --flake ~/config

# 설정 테스트
darwin-rebuild check --flake ~/config

# 롤백
darwin-rebuild switch --rollback

# 세대 관리
nix-env --list-generations --profile /nix/var/nix/profiles/system

설정 백업과 복원

# 설정 백업
cd ~/config
git add .
git commit -m "시스템 설정 업데이트"
git push

# 새 Mac에서 복원
git clone https://github.com/user/mac-config.git ~/config
cd ~/config
darwin-rebuild switch --flake .

C.8 특징과 제한사항

장점

  • macOS 설정의 선언적 관리
  • Homebrew와 자연스러운 통합
  • 설정 버전 관리 및 공유
  • 새 시스템 빠른 구성

제한사항

  • macOS 전용
  • 모든 시스템 설정을 다루지 못함
  • GUI 설정은 제한적
  • 일부 macOS 업데이트 시 재설정 필요

요약: nix-darwin은 macOS 개발 환경을 코드로 관리하여 팀 표준화와 환경 재현성을 제공합니다. Homebrew와의 통합으로 macOS 생태계의 장점을 유지하면서 설정 관리를 체계화할 수 있습니다.

Appendix D: Home Manager 기초

D.1 Home Manager 개념

사용자 환경 관리

Home Manager는 사용자 레벨의 환경과 설정을 Nix로 선언적으로 관리하는 도구입니다.

# 목적: 개인 환경을 코드로 정의
# 핵심 개념: 사용자 레벨 설정, dotfiles 관리
{
  # 패키지 설치
  home.packages = with pkgs; [
    git vim curl python3
  ];

  # 프로그램 설정
  programs.git = {
    enable = true;
    userName = "Developer";
    userEmail = "dev@example.com";
  };

  # 환경 변수
  home.sessionVariables = {
    EDITOR = "vim";
    BROWSER = "firefox";
  };
}

적용 범위:

  • 개인 패키지 관리
  • dotfiles 버전 관리
  • 프로그램 설정 통합

D.2 설치와 설정

Flake 기반 설치

# 목적: Home Manager Flake 구성
# 핵심 개념: 사용자별 구성, 모듈화
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
    home-manager = {
      url = "github:nix-community/home-manager/release-23.11";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { nixpkgs, home-manager, ... }: {
    homeConfigurations.developer = home-manager.lib.homeManagerConfiguration {
      pkgs = nixpkgs.legacyPackages.x86_64-linux;
      modules = [ ./home.nix ];
    };
  };
}

기본 설정

# 목적: Home Manager 기본 구성
# 핵심 개념: 사용자 정보, 상태 버전
{ config, pkgs, ... }:

{
  home.username = "developer";
  home.homeDirectory = "/home/developer";
  home.stateVersion = "23.11";

  programs.home-manager.enable = true;
}

D.3 개발 환경 구성

Python 개발 환경

# 목적: Python 개발자용 환경 설정
# 핵심 개념: 언어별 도구, 개발 워크플로우
{ config, pkgs, ... }:

{
  home.packages = with pkgs; [
    # Python 도구
    python3
    python3Packages.pip
    python3Packages.virtualenv

    # 개발 도구
    python3Packages.black
    python3Packages.flake8
    python3Packages.pytest

    # 에디터와 유틸리티
    git curl wget tree htop
  ];

  # Python 환경 변수
  home.sessionVariables = {
    PYTHONPATH = "$HOME/.local/lib/python3.11/site-packages";
    PIP_USER = "true";
  };

  # 별칭
  home.shellAliases = {
    py = "python3";
    pip = "pip3";
    venv = "python3 -m venv";
  };
}

웹 개발 환경

# 목적: 웹 개발자용 환경 설정
# 핵심 개념: 다중 언어, 빌드 도구
{
  home.packages = with pkgs; [
    # 런타임
    nodejs yarn
    python3

    # 도구
    git docker-client

    # 에디터 지원
    nodePackages.typescript
    nodePackages.eslint
  ];

  # 프로젝트별 설정
  programs.direnv = {
    enable = true;
    enableBashIntegration = true;
    nix-direnv.enable = true;
  };
}

D.4 프로그램 설정 관리

Git 설정

# 목적: Git 전역 설정 관리
# 핵심 개념: 버전 관리 설정, 워크플로우
{
  programs.git = {
    enable = true;
    userName = "Developer Name";
    userEmail = "dev@company.com";

    extraConfig = {
      init.defaultBranch = "main";
      push.default = "simple";
      pull.rebase = true;

      core.editor = "vim";
      merge.tool = "vimdiff";
    };

    aliases = {
      st = "status";
      co = "checkout";
      br = "branch";
      cm = "commit -m";
      lg = "log --oneline --graph";
    };

    ignores = [
      "*.pyc"
      "__pycache__/"
      ".venv/"
      ".DS_Store"
      "node_modules/"
    ];
  };
}

셸 환경

# 목적: 셸 환경 커스터마이징
# 핵심 개념: 생산성 향상, 자동화
{
  programs.bash = {
    enable = true;

    shellAliases = {
      ll = "ls -la";
      grep = "grep --color=auto";
      ".." = "cd ..";
    };

    initExtra = ''
      # 프롬프트 설정
      export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

      # 히스토리 설정
      export HISTSIZE=10000
      export HISTFILESIZE=20000
    '';
  };

  programs.zsh = {
    enable = true;
    enableCompletion = true;
    enableAutosuggestions = true;
    syntaxHighlighting.enable = true;

    oh-my-zsh = {
      enable = true;
      theme = "robbyrussell";
      plugins = [ "git" "python" "node" ];
    };
  };
}

에디터 설정

# 목적: 에디터 환경 설정
# 핵심 개념: 개발 도구 통합
{
  programs.vim = {
    enable = true;

    extraConfig = ''
      set number
      set tabstop=4
      set shiftwidth=4
      set expandtab

      syntax on
      filetype plugin indent on

      set hlsearch
      set incsearch
    '';
  };

  programs.vscode = {
    enable = true;
    extensions = with pkgs.vscode-extensions; [
      ms-python.python
      ms-vscode.vscode-typescript-next
      esbenp.prettier-vscode
    ];

    userSettings = {
      "editor.formatOnSave" = true;
      "python.defaultInterpreterPath" = "${pkgs.python3}/bin/python";
      "editor.tabSize" = 2;
    };
  };
}

D.5 프로젝트별 환경

direnv 통합

# 목적: 프로젝트별 자동 환경 전환
# 핵심 개념: 디렉토리 기반 환경
{
  programs.direnv = {
    enable = true;
    enableBashIntegration = true;
    enableZshIntegration = true;
    nix-direnv.enable = true;
  };
}

프로젝트에서 사용:

# .envrc 파일 생성
echo "use flake" > .envrc
direnv allow

개발 템플릿

# 목적: 일관된 프로젝트 구조 제공
# 핵심 개념: 템플릿화, 자동화
{
  home.file = {
    # 프로젝트 템플릿
    "templates/python/.envrc".text = "use flake";
    "templates/python/flake.nix".text = ''
      {
        inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
        outputs = { nixpkgs, ... }:
          let pkgs = nixpkgs.legacyPackages.x86_64-linux;
          in {
            devShells.x86_64-linux.default = pkgs.mkShell {
              buildInputs = with pkgs; [
                python3
                python3Packages.pytest
              ];
            };
          };
      }
    '';

    # 스크립트
    "bin/new-python-project" = {
      text = ''
        #!/bin/bash
        mkdir -p "$1"
        cd "$1"
        cp -r ~/templates/python/* .
        direnv allow
      '';
      executable = true;
    };
  };
}

D.6 환경 동기화

설정 백업

# Git 저장소로 설정 백업
cd ~/.config/home-manager
git init
git add .
git commit -m "초기 Home Manager 설정"
git remote add origin https://github.com/user/dotfiles.git
git push -u origin main

새 시스템에서 복원

# 설정 복제
git clone https://github.com/user/dotfiles.git ~/.config/home-manager
cd ~/.config/home-manager

# 환경 적용
home-manager switch --flake .

업데이트 관리

# 설정 업데이트
home-manager switch

# Flake 업데이트
nix flake update
home-manager switch --flake .

# 세대 관리
home-manager generations
home-manager switch --switch-generation 42

D.7 모듈화와 재사용

공통 모듈

# 목적: 재사용 가능한 설정 모듈
# 파일: common.nix
{ pkgs, ... }:

{
  home.packages = with pkgs; [
    git curl wget tree htop
  ];

  programs.git = {
    enable = true;
    extraConfig.init.defaultBranch = "main";
  };

  home.shellAliases = {
    ll = "ls -la";
    ".." = "cd ..";
  };
}

환경별 구성

# 목적: 환경별 특화 설정
# 파일: work.nix
{
  imports = [ ./common.nix ];

  home.packages = with pkgs; [
    docker-client kubectl
    slack discord
  ];

  programs.git = {
    userName = "Work Name";
    userEmail = "work@company.com";
  };
}

D.8 특징과 활용

장점

  • 사용자 레벨 선언적 관리
  • 시스템 관리자 권한 불필요
  • dotfiles 버전 관리
  • 프로젝트별 환경 자동화

제한사항

  • 시스템 레벨 설정 불가
  • GUI 프로그램 설정 제한적
  • 일부 프로그램 미지원

요약: Home Manager는 개인 개발 환경과 dotfiles를 체계적으로 관리하는 도구입니다. 사용자 레벨에서 재현 가능한 환경을 제공하여 개발 워크플로우를 표준화할 수 있습니다.