Ver2.5 R 패키지 개발, 너도 할 수 있어! 🚀 나만의 코드를 작품으로 만드는 여정

R 패키지 개발, 너도 할 수 있어! 🚀 나만의 코드를 작품으로 만드는 여정
스크립트 파일의 굴레에서 벗어나, 재사용과 공유가 가능한 나만의 R 패키지를 만들어봐요.
이 가이드 하나면, 당신도 오늘부터 R 패키지 개발자!
들어가며: 왜 우리는 R 패키지를 만들어야 할까? 🤔
안녕! R 좀 만져봤다 하는 우리 동지들. 아마 다들 비슷한 경험이 있을 거야. 어제 짰던 그 코드, 오늘 다시 쓰려니 어디에 저장했는지 가물가물...
분석 프로젝트마다 복사-붙여넣기 하던 `my_awesome_function.R` 파일이 수십 개가 되어버린 경험 말이야. 😱
이런 '코드 유목민' 생활, 이제 청산할 때가 됐어. 바로 R 패키지 개발을 통해서 말이지. "패키지 개발? 그거 완전 전문가들이나 하는 거 아냐?" 라고 생각했다면, 그 생각 잠시 접어둬도 좋아. 이 글은 바로 그런 당신을 위해 탄생했으니까.
R 패키지는 단순히 코드 덩어리를 예쁘게 포장하는 게 아니야. 이건 내 코드를 '작품'으로 만드는 과정이자, 미래의 나 자신과 동료들을 위한 최고의 선물이라고 할 수 있지.
- 코드 재사용성 극대화: 언제 어디서든 `library(myPackage)` 한 줄이면 끝! 더 이상 `source()` 함수를 찾아 헤맬 필요가 없어.
- 체계적인 관리: 함수, 데이터, 문서, 테스트 코드가 각자의 집에 깔끔하게 정리돼. 코드의 구조가 한눈에 들어오지.
- 쉬운 공유와 협업: GitHub에 올리면 전 세계 누구나 당신의 코드를 쓸 수 있어. 협업이 훨씬 수월해지는 건 물론이고.
- 품질 보증: 문서화와 테스트를 강제(?)하는 구조 덕분에 코드의 신뢰도가 수직 상승해. 버그 잡기도 훨씬 쉬워지지.
- 성장의 발판: 패키지를 만들다 보면 R에 대한 이해도가 정말 깊어져. 진정한 R 고수로 가는 지름길이랄까?
이 가이드는 R 좀 다뤄봤지만 패키지 개발은 막막했던 당신을 위해, 친구가 옆에서 알려주듯 친절하고 재미있게 모든 과정을 안내할 거야. RStudio와 몇 가지 마법 같은 도구들만 있으면 충분해. 우리 함께 스크립트 파일에 불과했던 코드 조각들에 생명을 불어넣고, 멋진 R 패키지로 탄생시키는 여정을 떠나보자! 준비됐어? 😉
Part 1: 장비부터 챙기자! 🛠️ 개발 환경 세팅 A to Z
자, 위대한 여정을 떠나기 전엔 장비 점검이 필수지! R 패키지 개발도 마찬가지야. 다행히도 우리에게 필요한 장비는 아주 간단해. 바로 R과 RStudio, 그리고 우리의 개발 과정을 멱살 잡고 캐리해 줄 두 명의 슈퍼스타, `devtools`와 `usethis` 패키지야.
필수 준비물: R과 RStudio
이 글을 보고 있다면 R과 RStudio는 이미 설치되어 있겠지? 혹시라도 아직이라면, 지금 바로 설치하고 와줘. R은 우리 모험의 기반이 되는 땅이고, RStudio는 그 위에서 우리가 마음껏 뛰어놀 수 있게 해주는 최첨단 베이스캠프니까.
- R: The Comprehensive R Archive Network (CRAN) 공식 홈페이지에서 최신 버전을 설치해줘.
- RStudio: RStudio Desktop (Open Source License) 버전을 다운로드하면 돼. 무료인데 기능은 차고 넘쳐.
최신 버전으로 유지하는 거, 잊지 마! 개발의 세계에서는 '최신'이 곧 '안정'일 때가 많거든.
우리의 치트키: devtools와 usethis 패키지 🦸♂️
과거의 R 패키지 개발은 꽤나 고되고 번거로운 작업이었어. 수많은 명령어와 파일 구조를 일일이 외우고 수동으로 만들어야 했지. 하지만 이제 우리에겐 '해들리 위컴'이라는 신이 내려준 두 개의 선물이 있어. 바로 `devtools`와 `usethis`야.
- `devtools` (Development Tools): 이름 그대로 개발에 필요한 모든 도구를 담고 있는 만능 공구함이야. 패키지를 설치하고, 테스트하고, 문서화하고, 체크하는 모든 과정을 이 친구가 도와줘.
- `usethis` (Use This): `devtools`의 절친이자 비서 같은 역할을 해. "이것 좀 만들어줘" 하고 말하면 뚝딱하고 파일이나 코드 구조를 생성해주는 마법사지. 예를 들어, "테스트 파일 만들어줘!" 하면 `usethis::use_test("my_test")` 한 줄이면 끝!
이 둘만 있으면 우리는 개발의 본질, 즉 '좋은 코드를 짜는 것'에만 집중할 수 있어. 자, 그럼 우리의 치트키를 장착해볼까?
RStudio의 콘솔 창에 아래 코드를 입력하고 실행해줘.
install.packages(c("devtools", "usethis"))
설치가 완료되면, 이제 우리는 패키지 개발을 위한 최강의 무기를 손에 넣은 거야. 앞으로 이 두 패키지의 함수들을 정말 지겹도록 사용하게 될 테니, `library(devtools)`와 `library(usethis)`는 마음속에 항상 새겨두자고.
미래를 위한 보험: Git과 GitHub 연동 💾
"버전 관리? 그거 꼭 해야 해?" 응, 꼭 해야 해. 아니, 무조건 해야 해. Git은 우리 코드의 모든 변경 사항을 기록해주는 타임머신이야. 어젯밤에 멀쩡히 돌아가던 코드가 오늘 갑자기 안 될 때, Git이 있다면 "어제 저녁 7시로 돌아가자!" 하고 외칠 수 있지. 마법 같지 않아?
GitHub는 이런 Git 저장소를 온라인에 올려두고 다른 사람들과 공유하고 협업할 수 있게 해주는 웹사이트야. 우리가 만든 패키지를 세상에 선보이는 가장 쉽고 빠른 방법이기도 해.
Git을 처음 설치하고 설정하는 과정은 이 가이드의 범위를 조금 벗어나. 하지만 걱정 마! 인터넷에 좋은 자료가 정말 많아. 'Git 설치하기', 'RStudio Git 연동' 같은 키워드로 검색하면 스크린샷까지 포함된 친절한 가이드들을 쉽게 찾을 수 있을 거야. 딱 30분만 투자해서 기본 설정을 끝내고 오자. 미래의 내가 고마워할 거야!
RStudio에서 Git과 GitHub를 연동하는 건 아주 간단해.
- Git 설치 확인: RStudio의 `Tools > Global Options > Git/SVN` 메뉴로 들어가 봐. 'Git executable' 경로가 잘 잡혀있다면 성공! 아니라면 Git을 설치하고 경로를 지정해줘야 해.
- GitHub 개인 접근 토큰(PAT) 발급: 예전에는 아이디/비밀번호로 됐지만, 이제는 보안을 위해 토큰을 써. GitHub 웹사이트의 `Settings > Developer settings > Personal access tokens`에서 발급받을 수 있어.
- R에 토큰 등록: `usethis`가 이 과정도 도와줘. 콘솔에 `usethis::create_github_token()`을 실행하면 토큰 발급 페이지로 바로 이동하고, 발급받은 토큰을 `gitcreds::gitcreds_set()` 함수를 이용해 RStudio에 저장하면 끝!
# GitHub 토큰 발급 페이지로 이동 (웹브라우저가 열림)
usethis::create_github_token()
# 발급받은 토큰을 RStudio에 저장 (붙여넣기하라는 창이 뜰 거야)
# 예: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
gitcreds::gitcreds_set()
이 과정이 조금 번거롭게 느껴질 수 있지만, 딱 한 번만 해두면 앞으로의 패키지 개발과 공유가 정말 편해져. 이제 모든 장비 점검이 끝났어. 드디어 우리만의 패키지를 만들러 가볼 시간이야! 🚀
Part 2: 내 코드에 집을 지어주자! 🏡 R 패키지의 기본 구조
자, 이제 본격적으로 우리 코드에게 안락한 집을 지어줄 시간이야. R 패키지는 그냥 R 스크립트 파일을 폴더에 모아둔 게 아니야. 정해진 규칙에 따라 파일을 배치해야 하는, 잘 설계된 '건축물'과 같아. 하지만 걱정할 필요 없어. 우리에겐 건축가 `usethis`가 있으니까!
첫 삽 뜨기: `create_package()`로 프로젝트 시작
패키지를 만들고 싶은 경로로 이동해서, RStudio 콘솔에 마법의 주문을 외워보자. 나는 `myFirstPackage`라는 이름의 패키지를 만들어 볼게. 이름은 네 마음대로 정해도 좋아. 단, 영어로, 띄어쓰기 없이!
# 원하는 경로를 입력하거나, RStudio의 Files 탭에서 경로를 설정한 뒤 실행해줘.
usethis::create_package("myFirstPackage")
이 명령어를 실행하면 어떤 일이 일어날까?
- `myFirstPackage`라는 이름의 새 폴더가 생겨.
- 그 폴더 안에 패키지에 필요한 최소한의 파일과 폴더 구조가 자동으로 짠! 하고 만들어져.
- 새로운 RStudio 세션이 열리면서, 방금 만든 패키지 프로젝트가 로드될 거야.
이게 바로 `usethis`의 위대함이야. 우리는 복잡한 구조를 외울 필요 없이, 단 한 줄의 코드로 패키지 개발의 첫발을 내디딘 거지. 이제 우리 앞에 펼쳐진 파일과 폴더들이 각각 어떤 역할을 하는지 탐험해볼 시간이야.
패키지의 심장: `R/` 폴더
이름 그대로, 우리가 작성하는 모든 R 함수 코드는 바로 이 `R/` 폴더 안에 `.R` 파일 형태로 저장돼. 패키지의 핵심 로직과 기능이 모두 여기에 담기는 거지. 지금은 비어있지만, 곧 우리의 멋진 함수들로 가득 채워질 공간이야. 보통 함수 하나당 하나의 `.R` 파일을 만들거나, 관련된 함수끼리 묶어서 하나의 파일로 만들기도 해.
패키지의 신분증: `DESCRIPTION` 파일 🆔
이 파일은 말 그대로 우리 패키지의 '신분증'이야. 패키지 이름이 뭔지, 누가 만들었는지, 버전은 몇인지, 어떤 다른 패키지를 필요로 하는지 등 패키지에 대한 모든 메타 정보가 담겨있어. `create_package()`를 실행하면 기본 템플릿이 만들어지는데, 이걸 우리 패키지에 맞게 수정해줘야 해.
파일을 열어보면 이런 내용이 보일 거야.
Package: myFirstPackage
Title: What the Package Does (One Line, Title Case)
Version: 0.0.0.9000
Authors@R:
person("First", "Last", email = "first.last@example.com", role = c("aut", "cre"))
Description: What the package does (one paragraph).
License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a
license
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.2.3
- Package: 패키지 이름. `create_package()` 할 때 정한 이름이 들어가.
- Title: 패키지의 역할을 한 줄로 요약하는 제목. 영문 Title Case(각 단어 첫 글자를 대문자로)로 쓰는 게 관례야. 예: `A Tidyverse-Style Package for Awesome Analysis`.
- Version: 패키지 버전. `devtools`는 보통 `x.y.z.9000` 형식을 추천해. 마지막 9000은 개발 버전이라는 뜻! 정식 릴리즈할 때 `1.0.0` 처럼 바꿔주면 돼.
- Authors@R: 패키지 제작자 정보. `person()` 함수를 이용해 이름, 이메일, 역할을 지정해. `aut`는 저자(author), `cre`는 관리자(creator)를 의미해. 보통 둘 다 지정하지.
- Description: Title보다 좀 더 상세하게 패키지를 설명하는 문단. 이 패키지가 왜 필요하고, 어떤 문제를 해결하는지 등을 적어주면 좋아.
- License: 이 패키지를 다른 사람이 어떤 조건 하에 사용할 수 있는지를 명시하는 라이선스. 코드를 공개할 거라면 반드시 지정해야 해! `usethis`를 쓰면 아주 쉬워. 예를 들어 MIT 라이선스를 적용하고 싶다면 콘솔에 `usethis::use_mit_license()` 라고 치기만 하면 돼.
- Encoding: 인코딩 방식. 한글 주석 등을 사용하려면 `UTF-8`은 필수!
- LazyData: 패키지에 포함된 데이터를 어떻게 로드할지에 대한 설정. `true`로 두면 `library()` 할 때 데이터가 메모리에 바로 올라오지 않고, 실제 사용할 때 로드돼서 효율적이야.
- Imports / Suggests: (지금은 없지만 곧 추가될) 이 패키지가 의존하는 다른 패키지 목록. `Imports`는 패키지 작동에 필수적인 것, `Suggests`는 비네트나 예제 코드 등 부가 기능에만 필요한 것들을 적어. 이 부분은 나중에 `usethis`가 자동으로 관리해줄 거야!
지금 바로 `DESCRIPTION` 파일을 열어서 Title, Authors@R, Description 부분을 채워보자. 라이선스도 `usethis::use_mit_license("Your Name")` 같은 명령어로 정해주고!
출입 관리소: `NAMESPACE` 파일 🚪
이 파일은 우리 패키지의 '출입 관리소' 역할을 해. 조금 복잡하게 들릴 수 있지만, 딱 두 가지만 기억하면 돼.
- 외부로 내보낼 함수(수출): 우리 패키지에 함수가 100개 있더라도, 사용자가 직접 쓸 수 있는 함수는 몇 개 안 될 수 있어. 이처럼 외부(사용자)에 공개할 함수들을 `export(my_awesome_function)` 와 같은 형태로 등록하는 곳이야.
- 외부에서 가져올 함수(수입): 우리 패키지가 다른 패키지(예: `dplyr`)의 함수(예: `filter()`)를 사용한다면, 그 사실을 `importFrom(dplyr, filter)` 와 같이 명시해줘야 해.
"이거 너무 복잡한데? 내가 직접 다 써야 해?"
천만에! 이 `NAMESPACE` 파일은 절대, 절대, 절대로 우리가 직접 수정하지 않아. 앞으로 배울 `roxygen2`라는 문서화 도구가 우리 코드에 있는 특별한 주석을 읽어서 자동으로 이 파일을 생성하고 관리해줄 거야. 우리는 그냥 "아, 이런 게 있구나" 하고 개념만 이해하고 넘어가면 돼. 완전 편하지? 😉
기타 파일들: `.Rproj`, `.Rbuildignore`, `man/`
- `myFirstPackage.Rproj`: RStudio 프로젝트 파일이야. 이 파일을 더블클릭하면 RStudio가 이 패키지 개발에 최적화된 상태로 열려. 작업 경로도 자동으로 설정되고 아주 편해.
- `.Rbuildignore`: 패키지를 빌드(설치 가능한 파일로 만드는 과정)할 때 포함시키지 않을 파일이나 폴더 목록을 적는 곳이야. 예를 들어, 개인적인 메모나 임시 파일들을 여기에 등록하면 최종 패키지에는 포함되지 않아.
- `man/`: 'manual'의 약자야. 함수 하나하나에 대한 상세한 도움말 파일(`.Rd` 확장자)이 저장되는 폴더야. `?mean` 처럼 우리가 R에서 도움말을 볼 때 나타나는 내용이 바로 이 폴더 안의 파일들이지. 이 폴더 역시 `roxygen2`가 자동으로 만들어주기 때문에 직접 건드릴 일은 없어.
어때? 처음엔 낯설어 보였던 파일과 폴더들이 이제 각자 어떤 역할을 하는지 조금 감이 오지? `usethis`가 뼈대를 만들어줬으니, 이제 우리는 가장 중요한 `R/` 폴더에 살을 붙이고, `roxygen2`로 멋지게 꾸며줄 일만 남았어. 다음 파트에서 본격적으로 함수를 만들고 문서화하는 방법을 알아보자!
Part 3: 코드는 거들 뿐, 진짜는 문서화! ✍️ roxygen2로 전문가처럼
자, 드디어 패키지의 심장인 `R/` 폴더를 채워볼 시간이야. 하지만 그냥 함수만 뚝딱 만들고 끝내면 '나만 아는 코드'가 될 뿐이야. 좋은 패키지는 '누가 봐도 이해하기 쉬운 코드'를 지향해야 해. 그리고 그 핵심에는 바로 문서화(Documentation)가 있어.
여기서 우리의 두 번째 영웅, `roxygen2` 패키지가 등판해. `roxygen2`는 코드 바로 위에 특별한 형식의 주석을 다는 것만으로, 앞서 봤던 `man/` 폴더의 도움말 파일(`.Rd`)과 `NAMESPACE` 파일을 자동으로 생성해주는 마법 같은 도구야.
첫 함수 만들기: `add_two_numbers`
백문이 불여일견! `R/` 폴더에 간단한 함수를 하나 만들어보자. `usethis`를 사용하면 함수 파일 생성도 아주 쉬워.
콘솔에 이렇게 입력해봐.
usethis::use_r("add_two_numbers")
이 명령은 `R/` 폴더 안에 `add_two_numbers.R`이라는 파일을 만들고, 그 파일을 바로 열어줘. 정말 친절하지? 이제 열린 파일에 간단한 덧셈 함수를 작성해보자.
add_two_numbers <- function(x, y) {
x + y
}
좋아, 함수는 완성됐어. 하지만 이 상태로는 아무것도 할 수 없어. 이 함수를 패키지 사용자가 쓸 수 있게 외부에 공개하고, 도움말도 만들어줘야 해. 바로 여기서 `roxygen2` 주석이 등장할 차례야.
roxygen2 주석: 코드에 날개를 달아주자
`roxygen2` 주석은 일반 주석(`#`)과 달리, `#'` 로 시작해. 이 특별한 주석 안에 `@` 기호와 함께 특정 '태그'를 사용해서 함수의 정보를 알려주는 거야.
방금 만든 `add_two_numbers` 함수 위에 아래와 같이 주석을 추가해보자. RStudio에서는 함수 안에 커서를 두고 `Ctrl + Alt + Shift + R` 단축키를 누르면 주석 템플릿을 자동으로 만들어줘서 더 편해!
#' Add Two Numbers
#'
#' This function takes two numbers and returns their sum. It's a very
#' simple example function for our first package.
#'
#' @param x A number.
#' @param y Another number.
#' @return The sum of \code{x} and \code{y}.
#' @examples
#' add_two_numbers(1, 2)
#' add_two_numbers(10, -5)
#' @export
add_two_numbers <- function(x, y) {
x + y
}
와, 뭔가 주석이 엄청 길어졌지? 하지만 이게 바로 전문가처럼 보이는 비결이야. 하나씩 차근차근 살펴보자.
- 첫 번째 줄 (Title): 함수 이름 바로 위에 있는 첫 번째 줄은 도움말의 제목이 돼. 간결하고 명확하게!
- 두 번째 단락 (Description): 한 줄 띄고 작성하는 단락은 함수에 대한 상세 설명이 돼.
- 세 번째 단락 (Details, 선택사항): 또 한 줄 띄고 작성하면 더 자세한 내용을 담는 Details 부분이 돼. 여기서는 생략했어.
@param [인자 이름] [설명]: 함수의 각 인자(parameter)가 무엇인지 설명해. 모든 인자에 대해 작성해주는 게 좋아.@return [설명]: 함수가 무엇을 반환(return)하는지 설명해.@examples: 이 함수를 어떻게 사용하는지 보여주는 예제 코드. 사용자들이 가장 좋아하는 부분이야! 이 코드는 실제로 실행 가능해야 해.@export: 이게 제일 중요해! 이 태그가 있어야만 `roxygen2`가 이 함수를 `NAMESPACE` 파일에 등록해서, 사용자가 `library(myFirstPackage)` 후에 `add_two_numbers()`를 직접 호출할 수 있게 해줘. 마법의 주문 같은 거지.
이 외에도 정말 많은 태그들이 있어. 예를 들어, 다른 패키지의 함수를 코드 안에서 사용할 때는 `#' @importFrom [패키지명] [함수명]` 같은 태그를 사용해서 `NAMESPACE`에 수입 목록을 추가할 수 있지. 이건 나중에 의존성 관리 파트에서 더 자세히 다룰게.
마법의 주문: `devtools::document()`
자, 이제 우리가 정성껏 작성한 `roxygen2` 주석이 실제 결과물로 바뀌는 순간을 목격할 시간이야. 콘솔에 아래의 명령어를 입력하거나, RStudio의 `Build` 탭에서 `Document` 버튼을 클릭해봐.
devtools::document()
이 명령어를 실행하면 `roxygen2`가 우리 패키지 안의 모든 `R/` 폴더 파일을 스캔해서, `#'` 주석을 읽고 두 가지 중요한 작업을 수행해:
- `man/add_two_numbers.Rd` 파일 생성: `man/` 폴더를 확인해봐. `add_two_numbers.Rd` 라는 파일이 생겼을 거야. 이 파일을 열어보면 R의 도움말 페이지에서 사용하는 특수한 문법으로 가득 차 있어. 우리가 `roxygen2` 주석에 썼던 내용이 그대로 변환된 거지.
- `NAMESPACE` 파일 업데이트: `NAMESPACE` 파일을 열어봐. `@export` 태그 덕분에 아래와 같은 라인이 추가된 걸 볼 수 있어.
이제 `add_two_numbers` 함수는 공식적으로 외부 세계와 소통할 준비를 마친 거야.export(add_two_numbers)
이제 모든 준비가 끝났으니, 우리 패키지를 실제로 설치하고 함수를 테스트해볼까? `devtools::install()` 또는 RStudio의 `Build` 탭에 있는 `Install and Restart` 버튼을 눌러봐.
devtools::install()
설치가 완료되고 R 세션이 재시작되면, 이제 우리 패키지는 R에 정식으로 등록된 거야. 콘솔에 직접 테스트해보자.
library(myFirstPackage)
add_two_numbers(5, 3)
# [1] 8
?add_two_numbers
어때? `add_two_numbers(5, 3)`이 8을 정확히 반환하고, `?add_two_numbers`를 실행하면 우리가 `roxygen2` 주석으로 작성했던 내용이 멋진 도움말 페이지로 나타나는 걸 볼 수 있어. 정말 뿌듯한 순간이지! 🎉
이게 바로 R 패키지 개발의 핵심 워크플로우야.
- `R/` 폴더에 함수를 작성하고 `roxygen2` 주석을 단다.
- `devtools::document()`를 실행해서 `NAMESPACE`와 `man/` 폴더를 업데이트한다.
- `devtools::install()`로 패키지를 설치하고 테스트한다.
- 수정할 부분이 생기면 1번으로 돌아가 반복한다.
이 과정을 계속 반복하면서 패키지에 살을 붙여나가는 거야. 처음에는 조금 어색할 수 있지만, 몇 번만 해보면 금방 익숙해질 거야. 이제 당신은 R 패키지에 함수를 추가하고, 문서화하고, 사용할 수 있게 만드는 가장 중요한 기술을 익혔어. 축하해! 🥳
Part 4: 혼자는 외로워! 🤝 다른 패키지와 협업하는 법 (의존성 관리)
우리가 만드는 모든 기능을 처음부터 끝까지 다 만들 수는 없어. 그건 바퀴를 재발명하는 것과 같지. R의 가장 큰 강점은 바로 CRAN에 있는 수많은 패키지 생태계야. 이미 만들어진 훌륭한 도구들을 잘 가져다 쓰는 것이 현명한 개발자의 덕목이지.
내 패키지가 다른 패키지의 함수를 사용할 때, 이걸 '의존성(Dependency)이 있다'고 말해. 그리고 이 의존성을 명확하게 관리하는 건 패키지 개발에서 정말, 정말 중요해. 왜냐하면 내 패키지를 설치하는 사용자 컴퓨터에 `dplyr`이 안 깔려있는데 내 코드가 `dplyr::filter()`를 호출하면 어떻게 될까? 당연히 에러가 나겠지! 💥
의존성 관리는 "제 패키지를 쓰려면, 이런이런 패키지들이 미리 설치되어 있어야 해요!"라고 R에게 알려주는 과정이야.
Imports vs. Suggests vs. Depends: 뭐가 다를까?
`DESCRIPTION` 파일에는 의존성을 명시하는 세 가지 주요 필드가 있어. `Imports`, `Suggests`, `Depends`. 많은 사람들이 헷갈려 하는데, 핵심만 기억하면 간단해.
- Imports: 99%의 경우, 우리는 이것만 쓸 거야. "내 패키지가 작동하려면 이 패키지들이 *필수적*으로 필요해요"라는 의미. `Imports`에 명시된 패키지는 내 패키지를 설치할 때 자동으로 함께 설치돼. 가장 표준적이고 강력 추천하는 방법이야.
- Suggests: "필수는 아닌데, 있으면 더 좋아요"라는 의미. 예를 들어, 패키지의 핵심 기능에는 필요 없지만, 비네트(사용 설명서)를 만들거나, 예제 코드를 실행하거나, 데이터를 시각화하는 함수처럼 부가적인 기능에만 필요한 패키지들을 여기에 넣어. `Suggests`에 있는 패키지는 자동으로 설치되지 않아.
- Depends: 옛날 방식이고, 지금은 거의 사용하지 않아. "이 패키지를 `library()` 하면, `Depends`에 있는 패키지들도 함께 `library()` 해주세요"라는 강력한 요구야. 다른 패키지의 환경을 오염시킬 수 있어서, 해들리 위컴은 "절대 쓰지 마세요(Almost never use)"라고 말했어. 우리도 그냥 이런 게 있었다는 것만 알고 잊어버리자.
`usethis::use_package()`로 스마트하게 의존성 추가하기
자, 그럼 `DESCRIPTION` 파일에 의존성을 어떻게 추가할까? 물론 직접 파일을 열어서 `Imports: dplyr` 이렇게 타이핑해도 돼. 하지만 우리에겐 더 스마트한 방법이 있지. 바로 `usethis`!
예를 들어, 우리 패키지가 `stringr` 패키지를 필요로 한다고 가정해보자. 콘솔에 이렇게 입력해봐.
usethis::use_package("stringr")
이 명령어를 실행하면 `usethis`가 `DESCRIPTION` 파일을 열어서 `Imports:` 필드에 `stringr`를 자동으로 추가해줘. 만약 `Imports`가 아니라 `Suggests`에 추가하고 싶다면?
usethis::use_package("ggplot2", type = "Suggests")
이렇게 `type` 인자를 지정해주면 돼. 정말 간단하지? 이렇게 `usethis`를 사용하면 오타를 낼 위험도 없고, 버전 명시 등 복잡한 규칙도 알아서 처리해주니 훨씬 안전하고 편해.
패키지 안에서 다른 패키지 함수 사용하는 법: `::`
의존성을 `DESCRIPTION`에 추가했다고 끝이 아니야. 이제 코드 안에서 실제로 그 패키지의 함수를 '어떻게' 호출할지가 중요해.
가장 좋은 방법은 `패키지명::함수명()` 형태로 사용하는 거야. 예를 들어, `stringr` 패키지의 `str_to_title()` 함수를 사용하고 싶다면, 코드에 `stringr::str_to_title("hello world")` 라고 명시적으로 적어주는 거지.
왜 이게 좋을까?
- 명확성: 코드를 읽는 누구나 "아, 이 `str_to_title` 함수는 `stringr` 패키지에서 온 거구나!" 하고 바로 알 수 있어. 가독성이 엄청나게 올라가.
- 충돌 방지: 만약 다른 패키지에도 우연히 `str_to_title`이라는 이름의 함수가 있다면? `::` 없이 그냥 썼다가는 어떤 함수가 호출될지 알 수 없어. `::`는 이런 이름 충돌(namespace conflict)을 완벽하게 막아줘.
자, 그럼 이걸 적용해서 새로운 함수를 하나 만들어볼까? `usethis::use_r("make_title")`로 새 파일을 만들고 아래처럼 코드를 작성해보자.
#' Make a String Title Case
#'
#' This function uses the \code{stringr} package to convert a string
#' to title case.
#'
#' @param text A character string.
#' @return A title-cased character string.
#' @importFrom stringr str_to_title
#' @export
#' @examples
#' make_title("hello world, this is my package!")
make_title <- function(text) {
stringr::str_to_title(text)
}
`@importFrom`으로 `NAMESPACE` 관리하기
위 코드의 `roxygen2` 주석을 잘 봐. 새로운 태그가 등장했지? 바로 `#' @importFrom stringr str_to_title` 이야.
이 태그는 `roxygen2`에게 "이 함수 파일은 `stringr` 패키지의 `str_to_title` 함수를 사용하니까, `NAMESPACE` 파일에 `importFrom(stringr, str_to_title)` 이라고 적어줘!" 라고 알려주는 역할을 해.
이제 `devtools::document()`를 실행하면, `NAMESPACE` 파일이 이렇게 업데이트될 거야.
# Generated by roxygen2: do not edit by hand
export(add_two_numbers)
export(make_title)
importFrom(stringr, str_to_title)
이렇게 `::`와 `@importFrom`을 함께 사용하는 것이 현재 R 패키지 개발에서 가장 권장되는 '베스트 프랙티스(Best Practice)'야. 코드의 명확성과 패키지의 안정성을 모두 잡는 방법이지.
Tidyverse 스타일 코딩에 익숙한 우리에게 파이프 연산자 `%>%`는 공기와도 같지. 패키지 안에서 파이프를 쓰려면 어떻게 해야 할까? `usethis`가 이것도 해결해줘!
콘솔에 딱 한 번만 아래 명령어를 실행해줘.
usethis::use_pipe()
이 명령은 두 가지 일을 해:
- `DESCRIPTION` 파일의 `Imports`에 `magrittr` 패키지를 추가해.
- `R/utils-pipe.R` 이라는 파일을 만들고, 그 안에 `%>%`를 패키지 내부로 가져오는 코드를 자동으로 생성해줘.
이제 우리는 `roxygen2` 주석에 `#' @importFrom magrittr %>%`를 추가할 필요 없이, 패키지 내 모든 함수에서 자유롭게 `%>%`를 사용할 수 있어. 완전 편하지? 😉
의존성 관리는 처음엔 조금 복잡하게 느껴질 수 있지만, `usethis`, `::`, `@importFrom` 이 세 가지 원칙만 기억하면 돼. 이 원칙들을 잘 지키면, 당신의 패키지는 다른 어떤 환경에서도 안정적으로 작동하는 '잘 만든' 패키지가 될 거야. 이제 당신의 패키지는 더 이상 외톨이가 아니야. R 생태계의 다른 훌륭한 패키지들과 어깨를 나란히 하는 동료가 된 거지!
Part 5: 패키지에 데이터를 싣고 📦: `data/`와 `R/sysdata.rda`
함수만 있는 패키지는 조금 심심하지. 때로는 예제용 데이터나, 함수 내부에서 사용하는 보조 데이터가 필요할 때가 있어. 예를 들어, `ggplot2` 패키지에는 `mpg`, `diamonds` 같은 예제 데이터셋이 포함되어 있어서 사용자들이 쉽게 함수를 테스트하고 배울 수 있지.
우리 패키지에도 데이터를 포함시킬 수 있어. R 패키지에서 데이터를 다루는 방법은 크게 두 가지로 나뉘어. 바로 사용자에게 공개하는 데이터와 패키지 내부에서만 사용하는 데이터야.
사용자용 데이터: `data/` 폴더에 보관하기
사용자가 패키지를 로드한 뒤, `data(my_data)` 명령어나 그냥 `my_data`를 입력했을 때 바로 쓸 수 있는 데이터를 만들고 싶다면, 이 데이터를 `data/` 폴더에 `.rda` 파일 형식으로 저장해야 해.
이 과정 역시 `usethis`와 함께라면 식은 죽 먹기야. `iris` 데이터셋을 우리 패키지에 `flower_data`라는 이름으로 포함시켜 본다고 가정해보자.
Step 1: R 환경에 데이터 객체 만들기
먼저, 포함시키고 싶은 데이터를 R 세션의 전역 환경(Global Environment)에 객체로 만들어야 해.
flower_data <- iris
# 실제로는 여러분이 직접 만든 데이터프레임이나 리스트 등이 되겠지.
# 예: my_analysis_data <- read.csv("path/to/my/data.csv")
Step 2: `usethis::use_data()`로 데이터 저장하기
이제 마법의 주문을 외울 차례야. 콘솔에 아래와 같이 입력해봐.
usethis::use_data(flower_data)
이 명령은 다음과 같은 일을 자동으로 처리해줘:
- `data/` 라는 폴더가 없다면 새로 만들어줘.
- `flower_data` 라는 R 객체를 `data/flower_data.rda` 파일로 저장해. `.rda`는 R 전용 압축 데이터 형식이야.
이게 끝이야! 정말 간단하지?
Step 3: 데이터 문서화하기 (강력 추천!)
데이터를 포함시켰다면, 이 데이터가 무엇인지 설명하는 문서를 만들어주는 게 좋아. 그래야 사용자들이 `?flower_data`를 통해 데이터의 출처나 각 변수의 의미를 알 수 있으니까.
`R/` 폴더에 `data.R` 같은 파일을 하나 만들고 (`usethis::use_r("data-documentation")` 사용), 그 안에 아래와 같은 형식으로 `roxygen2` 주석을 작성해. 변수 없이, 데이터셋 이름만 따옴표로 감싸서 적는 게 특징이야.
#' Iris Flower Dataset
#'
#' A classic dataset containing measurements of 3 species of iris flowers.
#' This is included as an example dataset in myFirstPackage.
#'
#' @format A data frame with 150 rows and 5 variables:
#' \describe{
#' \item{Sepal.Length}{Sepal length, in cm}
#' \item{Sepal.Width}{Sepal width, in cm}
#' \item{Petal.Length}{Petal length, in cm}
#' \item{Petal.Width}{Petal width, in cm}
#' \item{Species}{Factor with levels \code{setosa}, \code{versicolor}, \code{virginica}}
#' }
#' @source \url{https://en.wikipedia.org/wiki/Iris_flower_data_set}
"flower_data"
@format: 데이터의 형식(예: 데이터프레임)과 차원(행/열 개수)을 설명해.\describe{}: `\item{변수명}{설명}` 형식으로 각 변수(열)가 무엇을 의미하는지 상세히 기술할 수 있어.@source: 데이터의 출처를 밝힐 때 사용해. 웹사이트 URL이나 논문 정보 등을 적어주면 좋아.
이제 `devtools::document()`와 `devtools::install()`을 실행하고 나면, `?flower_data`로 멋진 도움말을 볼 수 있고, 콘솔에서 바로 `head(flower_data)`처럼 데이터를 사용할 수 있게 돼. 멋지지?
개발자용 내부 데이터: `R/sysdata.rda`에 숨겨두기
때로는 사용자에게 직접 노출될 필요는 없지만, 패키지 내부의 함수들이 작동하는 데 필요한 데이터가 있을 수 있어. 예를 들어, 특정 계산에 필요한 고정된 계수 테이블이나, 에러 메시지 템플릿 모음 같은 것들 말이야.
이런 데이터는 `data/` 폴더에 넣으면 사용자를 혼란스럽게 할 수 있어. 이럴 때 사용하는 것이 바로 `R/sysdata.rda` 파일이야. 이 파일 안에 저장된 데이터 객체들은 패키지가 로드될 때 함께 로드되어서, 패키지 내의 함수들이 자유롭게 접근할 수 있지만, 사용자에게는 직접 보이지 않아.
`usethis`는 이 작업도 당연히 도와줘. `use_data()` 함수에 `internal = TRUE` 옵션만 추가하면 돼.
예를 들어, 우리 패키지 함수에서 사용할 비밀(?) 메시지를 저장한다고 해보자.
# R 환경에 내부 데이터 객체 생성
internal_messages <- list(
welcome = "Welcome to myFirstPackage!",
error = "Oops, something went wrong."
)
# internal = TRUE 옵션으로 저장
usethis::use_data(internal_messages, internal = TRUE)
이 명령을 실행하면 `R/sysdata.rda` 파일이 생성(또는 업데이트)되고, 그 안에 `internal_messages` 객체가 저장돼. 이제 `R/` 폴더 안의 다른 함수들에서는 `internal_messages$welcome` 같은 코드로 이 데이터에 바로 접근할 수 있어. 사용자들은 이 객체의 존재조차 모르게 말이지!
만약 기존 `R/sysdata.rda` 파일에 새로운 데이터를 추가하고 싶다면, `overwrite = TRUE` 옵션을 함께 사용하면 돼.
another_internal_data <- c(a = 1, b = 2)
usethis::use_data(internal_messages, another_internal_data, internal = TRUE, overwrite = TRUE)
- 이 데이터를 사용자가 직접 보고 사용해야 하는가? (예: 예제 데이터)
➡️ Yes: `usethis::use_data(my_data)` (즉, `data/` 폴더) - 이 데이터는 오직 내 패키지 함수들만 사용하는가? (예: 내부 설정값, 룩업 테이블)
➡️ Yes: `usethis::use_data(my_internal_data, internal = TRUE)` (즉, `R/sysdata.rda`)
데이터를 패키지에 포함시키는 방법을 익혔으니, 이제 당신의 패키지는 훨씬 더 풍부하고 유용해질 수 있어. 사용자들을 위한 친절한 예제 데이터를 제공하거나, 복잡한 내부 로직을 깔끔한 데이터 테이블로 분리해서 코드의 가독성을 높일 수도 있지. 데이터는 패키지의 또 다른 강력한 무기가 될 거야!
Part 6: 버그는 못 참지! 🐞 `testthat`으로 철벽 방어하기
자, 우리 패키지에 멋진 함수들도 만들고, 데이터도 넣었어. 이제 슬슬 세상에 내놓을 준비가 된 것 같지? 하지만 잠깐! "내 컴퓨터에서는 잘 되는데?"는 개발자에게 가장 위험한 말이야. 내 코드가 다양한 상황에서 예상대로 정확하게 작동하는지 어떻게 보장할 수 있을까?
정답은 바로 '단위 테스트(Unit Testing)'에 있어. 단위 테스트는 함수 같은 코드의 작은 단위(unit)가 각각 올바르게 작동하는지 검증하는 자동화된 코드야. 그리고 R 세계에는 이 테스트를 아주 쉽고 재미있게 만들어주는 `testthat`이라는 멋진 패키지가 있지.
테스트 코드를 작성하는 건 귀찮은 일이 아니라, 미래의 나를 위한 최고의 보험이야. 나중에 코드를 수정했는데, 예전에 잘 되던 기능이 갑자기 망가지는 '회귀(regression)' 버그를 막아주거든. 테스트가 있다면, 코드 수정 후 테스트만 돌려보면 무엇이 잘못됐는지 바로 알 수 있어.
테스트 환경 설정: `usethis::use_testthat()`
우리 패키지에 `testthat`을 도입하는 것 역시 `usethis`와 함께라면 단 한 줄로 끝나.
usethis::use_testthat()
이 명령은 다음과 같은 구조를 만들어줘:
- `DESCRIPTION` 파일의 `Suggests` 필드에 `testthat`을 추가해. (테스트는 개발 과정에만 필요하므로 `Imports`가 아닌 `Suggests`가 적절해.)
- `tests/` 라는 최상위 폴더를 만들어.
- 그 안에 `tests/testthat/` 라는 하위 폴더를 만들어. 앞으로 우리의 모든 테스트 파일은 바로 여기에 저장될 거야.
- `tests/testthat.R` 이라는 파일을 만들어서, 테스트를 실행하기 위한 기본 설정 코드를 넣어줘.
이제 우리 패키지는 테스트를 맞이할 모든 준비를 마쳤어.
첫 테스트 코드 작성하기
테스트를 추가하고 싶은 함수가 있다면, `usethis`에게 말만 하면 돼. 예를 들어, 우리가 만들었던 `add_two_numbers` 함수에 대한 테스트를 만들어보자.
usethis::use_test("add_two_numbers")
이 명령은 `tests/testthat/test-add_two_numbers.R` 라는 파일을 만들고 열어줘. 파일 이름은 `test-`로 시작하는 게 규칙이야. 파일 안에는 기본적인 템플릿이 들어있을 거야.
test_that("multiplication works", {
expect_equal(2 * 2, 4)
})
이게 `testthat` 테스트의 기본 구조야. 이걸 우리 함수에 맞게 수정해보자.
test_that("add_two_numbers works with positive numbers", {
expect_equal(add_two_numbers(2, 3), 5)
expect_equal(add_two_numbers(10, 100), 110)
})
test_that("add_two_numbers works with negative and zero", {
expect_equal(add_two_numbers(-5, 5), 0)
expect_equal(add_two_numbers(10, -20), -10)
expect_equal(add_two_numbers(0, 0), 0)
})
test_that("add_two_numbers handles non-numeric input", {
expect_error(add_two_numbers("a", 5))
expect_error(add_two_numbers(10, "b"))
})
test_that("설명", { ... }): 관련된 테스트들을 하나로 묶어주는 블록이야. 첫 번째 인자로 들어가는 "설명"은 테스트가 실패했을 때 어떤 테스트에서 문제가 생겼는지 알려주는 중요한 역할을 해.expect_*()함수들: 실제 테스트를 수행하는 '기대(expectation)' 함수들이야. "나는 이 코드가 이런 결과를 낼 것이라고 기대한다!"라고 선언하는 거지.expect_equal(결과, 기대값): 두 값이 같은지 확인해. 가장 많이 사용돼.expect_true(조건)/expect_false(조건): 조건이 참/거짓인지 확인해.expect_error(코드): 해당 코드를 실행했을 때 에러가 발생하는지 확인해. 잘못된 입력값에 대한 방어 로직을 테스트할 때 필수적이야.expect_warning(코드)/expect_message(코드): 경고나 메시지가 발생하는지 확인해.- 이 외에도 `expect_length()`, `expect_type()`, `expect_s3_class()` 등 수많은 `expect` 함수들이 있어. 상황에 맞게 골라 쓰면 돼.
테스트 실행하고 결과 확인하기: `devtools::test()`
테스트 코드를 작성했으니, 이제 실행해볼 시간이야. 콘솔에 아래 명령어를 입력하거나, RStudio의 `Build` 탭에서 `Test Package`를 클릭해봐.
devtools::test()
이 명령을 실행하면 `testthat`이 `tests/testthat/` 폴더의 모든 `test-*.R` 파일을 찾아 실행하고, 그 결과를 예쁘게 보여줘.
모든 테스트를 통과했다면, 아마 이런 뿌듯한 메시지를 보게 될 거야.
[1] "add_two_numbers works with positive numbers"
[1] "add_two_numbers works with negative and zero"
[1] "add_two_numbers handles non-numeric input"
-- Test summary ----------------------------------------------------------------
PASS: 3 | FAILED: 0 | WARN: 0 | SKIP: 0
만약 테스트가 실패하면, 어떤 `test_that` 블록의 어떤 `expect` 함수에서 문제가 발생했는지, 실제 결과값과 기대값이 어떻게 달랐는지 아주 상세하게 알려줘. 덕분에 우리는 버그를 훨씬 빠르고 정확하게 찾아 수정할 수 있지.
최종 관문: `devtools::check()` 👑
단위 테스트가 개별 부품을 검사하는 과정이었다면, `devtools::check()`는 완성된 자동차가 도로에 나가기 전에 모든 부품이 잘 조립되었는지, 안전 기준을 통과하는지 종합적으로 검사하는 것과 같아.
이 명령은 CRAN(R 패키지 공식 저장소)에 패키지를 제출할 때 거치는 검사 과정을 내 컴퓨터에서 그대로 시뮬레이션해줘. `check()`는 정말 많은 것들을 검사해:
- `DESCRIPTION` 파일의 형식은 올바른가?
- 모든 함수에 문서(도움말)가 있는가?
- 예제 코드는 에러 없이 실행되는가?
- `testthat` 테스트는 모두 통과하는가?
- 코드에 흔히 발생하는 문제점은 없는가?
- 패키지 파일 구조는 표준을 따르는가? ... 등등 수십 가지 항목을 체크해.
콘솔에 입력하거나, RStudio의 `Build` 탭에서 `Check` 버튼을 눌러 실행해보자.
devtools::check()
검사가 끝나면 결과를 세 가지 등급으로 알려줘: `ERROR`, `WARNING`, `NOTE`.
- ERRORs: 치명적인 문제. 반드시 해결해야 해. 에러가 있으면 CRAN에 제출할 수 없어.
- WARNINGs: 심각한 문제. 대부분의 경우 반드시 해결해야 해.
- NOTEs: 잠재적인 문제일 수 있거나, CRAN 정책에 대한 제안 사항. 경우에 따라 무시할 수도 있지만, 가능한 한 모두 해결해서 '0 errors, 0 warnings, 0 notes' 상태를 만드는 것이 가장 좋아.
`check()`를 통과하는 것은 패키지의 품질을 보증하는 '품질 보증 마크'와 같아. 내 패키지를 다른 사람에게 자신 있게 공유하고 싶다면, `check()`를 습관화하는 것이 좋아. 코드를 조금 수정할 때마다 `check()`를 돌려보는 습관을 들인다면, 당신은 이미 훌륭한 R 패키지 개발자의 길을 걷고 있는 거야!
Part 7: 사용 설명서는 친절하게! 📖 비네트(Vignette)와 README 작성법
우리가 만든 패키지는 마치 새로운 가전제품과 같아. 기능이 아무리 훌륭해도 사용 설명서가 엉망이면 아무도 제대로 쓸 수 없지. R 패키지에서 이런 '사용 설명서' 역할을 하는 두 가지 중요한 문서가 바로 비네트(Vignette)와 README야.
개별 함수에 대한 도움말(`?function_name`)이 특정 버튼의 기능을 설명하는 것이라면, 비네트와 README는 제품 전체를 어떻게 활용하는지 보여주는 종합 가이드북이라고 할 수 있어.
장문의 튜토리얼: 비네트(Vignette) 만들기
비네트는 패키지의 주요 기능을 소개하고, 실제 문제를 해결하는 과정을 처음부터 끝까지 보여주는 긴 형식의 문서야. 보통 R 마크다운(`.Rmd`) 형식으로 작성되기 때문에, 설명 텍스트와 실행 가능한 R 코드, 그리고 그 결과(표, 플롯 등)를 한 문서 안에 모두 담을 수 있어.
사용자들은 비네트를 통해 패키지의 철학을 이해하고, 여러 함수들을 어떻게 조합해서 사용하는지 배울 수 있어. 잘 만든 비네트 하나가 열 개의 함수 도움말보다 더 효과적일 때가 많아.
자, 우리 패키지의 첫 비네트를 만들어볼까? 역시 `usethis`와 함께!
usethis::use_vignette("myFirstPackage-intro")
이 명령은:
- `DESCRIPTION` 파일의 `Suggests`에 `knitr`, `rmarkdown`을 추가하고, `VignetteBuilder` 필드를 설정해줘.
- `vignettes/` 폴더를 만들고, 그 안에 `myFirstPackage-intro.Rmd` 라는 R 마크다운 템플릿 파일을 생성해줘.
이제 `vignettes/myFirstPackage-intro.Rmd` 파일을 열어보자. 상단에는 YAML 헤더가 있고, 그 아래에는 R 마크다운 문법으로 내용을 채울 수 있는 공간이 있어.
---
title: "Introduction to myFirstPackage"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Introduction to myFirstPackage}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
```{r setup}
library(myFirstPackage)
```
여기에 여러분의 패키지를 소개하는 멋진 글과 예제 코드를 작성하면 됩니다.
R 마크다운 문법을 그대로 사용할 수 있어요!
예를 들어, `add_two_numbers()` 함수는 이렇게 사용합니다.
```{r}
add_two_numbers(5, 10)
```
`make_title()` 함수를 사용하면 문장을 예쁘게 바꿀 수 있죠.
```{r}
text_example <- "this is a title"
make_title(text_example)
```
YAML 헤더의 `title`을 적절히 수정하고, 본문에 패키지 설치 방법, 주요 함수 소개, 실제 데이터 분석 예시 등을 자유롭게 작성하면 돼. R 마크다운을 써봤다면 아주 익숙할 거야.
비네트를 작성한 후에는 `devtools::build_vignettes()`를 실행해서 `.Rmd` 파일을 HTML 문서로 빌드할 수 있어. `devtools::install()`을 실행할 때도 비네트는 자동으로 빌드돼. 설치가 완료된 후 `browseVignettes("myFirstPackage")` 명령을 실행하면, 우리가 만든 멋진 튜토리얼을 웹 브라우저에서 확인할 수 있어!
패키지의 얼굴: README 파일 만들기
README 파일은 우리 패키지의 '얼굴'이자 '대문'이야. 특히 GitHub 저장소에 방문한 사람들이 가장 먼저 보게 되는 파일이지. 이 패키지가 무엇을 하는지, 어떻게 설치하는지, 가장 기본적인 사용법은 무엇인지를 간결하게 보여줘야 해.
README 역시 R 마크다운으로 작성해서 코드 실행 결과를 포함시키는 게 요즘 트렌드야. `usethis`는 이 작업도 기가 막히게 도와줘.
usethis::use_readme_rmd()
이 명령은 프로젝트 최상위 경로에 `README.Rmd` 파일을 생성해줘. 이 파일을 수정하고 내용을 채운 뒤, `devtools::build_readme()`를 실행하면 `README.Rmd` 파일이 일반 마크다운 파일인 `README.md`로 변환(knit)돼. GitHub는 `README.md` 파일을 자동으로 인식해서 예쁘게 보여주거든.
- 패키지 이름과 짧은 설명: 이 패키지가 무엇을 위한 것인지 한두 문장으로 요약.
- (선택) 상태 배지(Badges): CRAN 버전, 테스트 통과 여부(CI/CD), 코드 커버리지 등을 보여주는 작은 이미지. 패키지의 신뢰도를 높여줘. `usethis::use_cran_badge()`, `usethis::use_github_action_check_standard()` 등으로 쉽게 추가할 수 있어.
- 설치 방법(Installation): CRAN이나 GitHub에서 어떻게 설치하는지 명확한 코드와 함께 안내.
# CRAN에서 설치 (아직 제출 전이라면 주석 처리) # install.packages("myFirstPackage") # GitHub 개발 버전 설치 # devtools::install_github("your-github-username/myFirstPackage") - 기본 사용 예제(Example): 가장 핵심적인 기능을 보여주는 간단하고 명확한 예제 코드. "이 패키지를 쓰면 5분 안에 이런 걸 할 수 있어요!"를 보여주는 게 목표야.
- (선택) 도움받는 방법, 기여하는 방법, 행동 강령(Code of Conduct) 등.
`README.Rmd`를 수정한 뒤에는 잊지 말고 `devtools::build_readme()`를 실행해서 `README.md`를 업데이트해줘야 해. 이 과정을 자동화하고 싶다면, Git에 커밋하기 전에 자동으로 README를 빌드해주는 'pre-commit hook'을 설정할 수도 있어 (`usethis::use_precommit()` 참고).
친절한 비네트와 일목요연한 README는 당신의 패키지를 그냥 '쓸만한 코드 모음'에서 '사용자 친화적인 훌륭한 소프트웨어'로 격상시켜주는 중요한 마지막 터치야. 코드 작성만큼이나 문서화에도 애정을 쏟는다면, 사용자들은 당신의 패키지를 사랑하게 될 거야. ❤️
Part 8: 세상에 너의 코드를 알려라! 📢 GitHub와 CRAN 배포
자, 드디어 대망의 마지막 단계야. 우리는 열심히 패키지를 만들고, 문서화하고, 테스트까지 마쳤어. 이제 이 보석 같은 패키지를 나만 알고 있기엔 너무 아깝지 않아? 이제 당신의 코드를 세상에 공개하고 다른 사람들도 사용할 수 있게 만들어 줄 시간이야.
R 패키지를 배포하는 방법은 크게 두 가지야. 하나는 빠르고 쉬운 GitHub를 이용하는 방법, 다른 하나는 R의 공식 인증을 받는 것과 같은 CRAN에 제출하는 방법이야.
방법 1: 빠르고 자유로운 GitHub 배포 🚀
GitHub는 우리 패키지의 집이자, 쇼케이스이자, 배포 채널이 될 수 있는 가장 강력하고 쉬운 방법이야. Part 1에서 Git/GitHub 설정을 마쳤다면, 배포는 정말 간단해.
Step 1: GitHub 저장소(Repository) 생성 및 연결
아직 GitHub에 이 패키지를 위한 저장소를 만들지 않았다면, `usethis`에게 부탁하자.
usethis::use_github()
이 마법 같은 함수는:
- 당신의 GitHub 계정에 패키지 이름과 동일한 이름의 새로운 public 저장소를 만들어줘.
- 로컬 Git 저장소와 방금 만든 원격 GitHub 저장소를 연결해줘.
- 지금까지 작업한 모든 내용을 GitHub에 푸시(push)해줘.
이제 GitHub의 당신 계정에 가보면 `myFirstPackage` 저장소가 짠! 하고 만들어져 있고, 우리가 작성한 `README.md` 파일이 멋지게 첫 페이지를 장식하고 있을 거야.
Step 2: 설치 방법 안내하기
이제 전 세계 누구나 `devtools` 패키지만 있으면 당신의 패키지를 설치할 수 있어. `README.md` 파일에 아래와 같이 설치 방법을 안내해주자.
# 먼저 devtools 패키지를 설치해야 합니다.
# install.packages("devtools")
# GitHub에서 myFirstPackage를 설치합니다.
devtools::install_github("YOUR_GITHUB_USERNAME/myFirstPackage")
`YOUR_GITHUB_USERNAME` 부분만 당신의 GitHub 아이디로 바꿔주면 돼. 정말 간단하지?
GitHub를 통한 배포는 수정 사항을 즉시 반영할 수 있고, CRAN의 엄격한 정책에 얽매이지 않아도 된다는 큰 장점이 있어. 개인 프로젝트나 빠르게 발전하는 개발 단계의 패키지에 아주 적합한 방법이야.
방법 2: R 공식 인증, CRAN 배포 🏆
CRAN(The Comprehensive R Archive Network)은 R의 공식 패키지 저장소야. `install.packages("dplyr")` 처럼 패키지 이름만으로 간단하게 설치할 수 있는 모든 패키지는 바로 이 CRAN에 등록된 것들이지. CRAN에 패키지를 등록한다는 것은 당신의 패키지가 R 생태계의 공식적인 일원이 된다는 의미이며, 상당한 수준의 품질과 안정성을 인정받았다는 뜻이야. 그야말로 R 패키지 개발자의 '명예의 전당'이라고 할 수 있지.
하지만 그만큼 과정이 까다롭고 엄격해. CRAN은 자원봉사자들로 이루어진 팀이 관리하는데, 전 세계에서 쏟아지는 패키지들을 일관된 기준으로 관리해야 하기 때문이야.
CRAN 제출 전 최종 점검 리스트 ✅
CRAN에 도전하기로 마음먹었다면, 아래 항목들을 반드시 통과해야 해.
- `devtools::check()` 통과: `0 errors, 0 warnings, 0 notes`를 목표로! 만약 NOTE가 있다면, 왜 그 NOTE가 발생했고 문제가 되지 않는지 CRAN 리뷰어에게 설명할 준비를 해야 해.
- 다양한 환경에서 테스트: 내 컴퓨터에서만 `check()`를 돌려보는 건 부족해. `devtools`는 다른 운영체제(Windows, macOS, Linux)와 다른 R 버전(개발 버전 등)에서 원격으로 `check()`를 실행해볼 수 있는 기능을 제공해.
이 과정에서 예상치 못한 에러를 발견할 수 있어.# Windows 개발 버전에서 체크 devtools::check_win_devel() # R-hub 빌더를 이용한 다양한 환경 체크 rhub::check_for_cran() - CRAN 정책 숙지: CRAN은 'CRAN Repository Policy'라는 자체 정책 문서를 가지고 있어. 패키지 이름 규칙, `DESCRIPTION` 파일 작성법, 코드 작성 시 유의사항 등 꽤 상세한 내용이 담겨있어. 제출 전에 한 번쯤은 꼭 읽어보는 게 좋아.
- 완벽한 문서화: 모든 함수와 데이터는 문서화되어야 하고, 예제 코드는 모두 에러 없이 실행되어야 해. 패키지의 목적과 사용법을 설명하는 비네트가 있다면 CRAN 팀이 아주 좋아해.
제출 과정: `devtools::release()`
이 모든 준비를 마쳤다면, `devtools`의 `release()` 함수가 제출 과정을 안내해줄 거야.
devtools::release()
이 함수를 실행하면, 마치 로켓 발사 전 체크리스트처럼 여러 가지 질문을 던지며 최종 점검을 도와줘. `DESCRIPTION` 파일의 버전은 올바른지, `NEWS.md` 파일(버전별 변경사항을 기록하는 파일, `usethis::use_news_md()`로 생성)은 업데이트했는지 등을 확인하고, 최종적으로 패키지를 압축 파일(`.tar.gz`)로 만들어 CRAN에 제출할 준비를 마쳐줘. 모든 과정에 동의하면, 웹 브라우저가 열리면서 CRAN 제출 양식 페이지로 이동하고, 필요한 정보를 입력한 뒤 압축 파일을 업로드하면 제출이 완료돼.
제출 후에는 CRAN 팀의 리뷰를 기다려야 해. 며칠에서 몇 주가 걸릴 수 있어. 수정 요청이 올 수도 있고, 운이 좋으면 바로 통과될 수도 있어. 첫 제출에 거절당하더라도 너무 실망하지 마. 피드백을 반영해서 다시 도전하면 돼. 이 과정을 통해 당신의 패키지는 더욱 단단해질 거야.
이러한 패키지 개발 및 배포 기술은 매우 전문적인 능력이기 때문에, 익숙해진다면 재능넷과 같은 플랫폼에서 당신의 전문 지식을 필요로 하는 사람들에게 도움을 주며 새로운 기회를 만들 수도 있을 거야. 🌟
마치며: 이제 당신도 R 패키지 개발자! 🎉 다음 스텝은?
와, 정말 긴 여정이었어! 축하해! 👏 이 가이드를 끝까지 따라왔다면, 당신은 이제 더 이상 `source()` 함수에 의존하던 과거의 내가 아니야. 당신은 이제 체계적인 구조 위에서 코드를 작성하고, 문서화하고, 테스트하고, 세상과 공유할 줄 아는 진정한 R 패키지 개발자야.
우리는 함께 R 패키지 개발 환경을 세팅하는 것부터 시작해서, `usethis`로 패키지의 뼈대를 만들고, `R/` 폴더에 함수를 채워 넣었어. `roxygen2`로 전문가처럼 문서화를 하고, `testthat`으로 코드의 안정성을 확보했지. `devtools::check()`라는 최종 관문을 통과하는 법도 배웠고, 마침내 GitHub와 CRAN을 통해 우리의 작품을 세상에 선보이는 방법까지 모두 경험했어.
이 모든 과정이 처음에는 낯설고 복잡하게 느껴질 수 있어. 하지만 기억해. 가장 중요한 것은 '완벽함'이 아니라 '시작하는 것'과 '꾸준히 개선해 나가는 것'이야.
- 작게 시작하기: 처음부터 세상을 바꿀 거대한 패키지를 만들려고 하지 마. 평소에 자주 사용하던 3~4개의 함수를 묶어서 작은 패키지로 만드는 것부터 시작해봐.
- 다른 사람의 코드 읽기: `tidyverse` 팀의 패키지(dplyr, ggplot2 등)나 당신이 좋아하는 다른 패키지들의 GitHub 저장소에 가서 코드를 읽어봐. 다른 고수들은 패키지를 어떻게 구성하고, 어떤 방식으로 문제를 해결하는지 배우는 것만큼 좋은 공부는 없어.
- 지속적 통합(CI) 도입하기: GitHub Actions 같은 CI/CD 도구를 사용하면, 코드를 GitHub에 푸시할 때마다 자동으로 `devtools::check()`를 실행하고 테스트를 돌릴 수 있어. 패키지의 품질을 항상 최상으로 유지하는 데 큰 도움이 돼. `usethis`에는 `usethis::use_github_action()` 같은 함수들이 준비되어 있어.
- 커뮤니티와 함께하기: 막히는 부분이 있다면 주저하지 말고 질문해. RStudio 커뮤니티, Stack Overflow, 트위터의 #rstats 해시태그 등 도움을 받을 수 있는 곳은 정말 많아. 당신의 경험을 공유하는 것도 잊지 마!
R 패키지 개발은 단순히 코드를 정리하는 기술이 아니야. 이건 더 나은 프로그래머가 되기 위한 훈련 과정이고, 재현 가능한 과학(reproducible research)을 실천하는 방법이며, R 커뮤니티에 기여하는 가장 멋진 방법 중 하나야.
이제 당신의 손에는 스크립트 파일들을 멋진 작품으로 바꿀 수 있는 강력한 도구들이 쥐어져 있어. 당신의 아이디어와 코드를 패키지라는 그릇에 담아 세상과 나눠봐. 당신의 작은 패키지가 누군가의 반복적인 작업을 자동화해주고, 복잡한 분석을 가능하게 만들어 줄지도 몰라. 그 과정에서 느끼는 뿌듯함과 즐거움은 코딩의 또 다른 차원의 재미를 선사해 줄 거야.
이 가이드가 당신의 첫 패키지를 만드는 데 든든한 친구가 되었기를 바라. 이제 진짜 시작이야. Happy packaging! 😊
댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
아직 댓글이 없습니다.