News Feed

“요새 뜨는 SNS” 블루스카이 북마크 R로 검색하기

컨텐츠 정보

  • 조회 744

본문

X(구 트위터)의 변화에 대한 거부감에 힘입어 블루스카이 소셜 네트워크가 부상하고 있다. GE 헬스케어(GE HealthCare)의 AI 지원 글로벌 책임자인 얀 베거는 지난달 링크드인에 “오염되지 않은 낙원이라고 할 수는 없지만 신선한 느낌이 있다. 공기가 조금 더 맑고 사람들이 친근하게 반겨주는 새로운 동네로 이사하는 것과 비슷하다”고 썼다.

#rstats 해시태그를 즐겨 사용하는 R 사용자들도 이 흐름에 동참하고 있다. 포싯(Posit. 전 R스튜디오)은 12월 뉴스래터에서 “블루스카이가 뜨고 있습니다! 편리한 포싯 직원 스타터 팩을 사용해 참여하세요”라고 썼다. 스타터 팩을 사용하면 주제와 관심 사항에 따라 다른 블루스카이 사용자를 한 번의 클릭으로, 또는 하나씩 손쉽게 찾고 팔로우할 수 있다. 누구나 스타터 팩을 만들 수 있다(블루스카이 FAQ 기사 참조).

다만 블루스카이는 비교적 새로운 플랫폼인 만큼(작년 초에 일반에 공개됨) 다른 마이크로 블로깅 플랫폼에서는 표준으로 제공되는 몇 가지 기능이 아직 없다. 없어서 가장 아쉬운 기능은 내가 ‘좋아요’를 누른 포스트를 저장해서 나중에 쉽게 찾아볼 수 있게 해주는 “북마크”다. 임시방편으로 써드 파티 앱 또는 맞춤형 피드를 사용해야 한다. 또한 블루스카이의 무료 API와 atrrr R 패키지 덕분에 R을 사용해서 ‘좋아요’를 누른 모든 포스트의 데이터를 로컬에 저장하고 원하는 대로 쿼리할 수 있다. 방법은 다음과 같다.

블루스카이 ‘좋아요’ 저장과 검색

이 프로젝트를 사용하면 블루스카이 ‘좋아요’를 다운로드하고 데이터를 랭글링하고 저장하고 검색 가능한 테이블에 로드할 수 있다. 또한 데이터를 스프레드시트에 저장해 북마크로 사용할 부분을 수동으로 표시해서 해당 항목만 검색할 수 있다.

결과를 생성형 AI 챗봇으로 보내면 특정 단어를 검색하는 대신 자연어로 질문할 수도 있다.

시작해 보자.

1단계. atrrr R 패키지 설치

깃허브에서 atrrr R 패키지 개발 버전을 설치하는 것이 좋다. 필자는 깃허브에서 설치하는 경우 보통 remotes 패키지를 사용하지만 devtools, pak 등을 사용해도 된다.

remotes::install_github("JBGruber/atrrr", build_vignettes = TRUE)

데이터 랭글링에 dplyr, purrr, stringr, tidyr, rio를 사용하고 데이터를 검색 가능한 테이블에 표시하는 용도로는 DT를 사용한다. CRAN에서 이러한 요소를 설치한 후 다음 코드를 사용해 모두 로드한다.

library(atrrr)library(dplyr)library(purrr)library(stringr)library(tidyr)library(rio)library(DT) # optional

블루스카이 API에서 데이터를 요청하려면 무료 블루스카이 애플리케이션 비밀번호(계정 비밀번호와는 별개임)가 필요하다. 기본 브라우저에서 이미 블루스카이에 로그인했다면 atrrr을 통해 처음 요청을 수행할 때 앱 비밀번호를 추가하기 위한 블루스카이 웹사이트가 열린다. 또는 여기로 가서 미리 앱 비밀번호를 만들어도 된다.

어떤 방법으로 비밀번호를 생성하든 atrrr 패키지를 처음 사용할 때 이미 저장된 애플리케이션 비밀번호가 없는 경우 입력하라는 메시지가 표시된다. 비밀번호를 입력하면 토큰이 생성되어 file.path(tools::R_user_dir("atrrr", "cache"), Sys.getenv("BSKY_TOKEN", unset = "token.rds"))의 캐시 디렉터리에 저장된다. 이후 요청에서는 자동으로 이 토큰에 액세스한다.

블루스카이 인증에 대한 더 자세한 내용은 atrrr 패키지 웹사이트에서 볼 수 있다.

2단계. 블루스카이 ‘좋아요’ 다운로드와 랭글링

get_actor_likes() 함수를 사용하면 사용자의 ‘좋아요’를 다운로드할 수 있다. 가장 최근 100개의 ‘좋아요’를 다운로드하는 방법은 다음과 같다.

my_recent_likes 

참고 : 이 기사를 작성하는 현재 실제보다 더 많은 ‘좋아요’를 요청하면 오류가 발생한다. 오류가 발생하면 숫자를 줄여서 다시 시도하면 된다.

위의 get_actor_likes()명령은 20개의 열이 포함된 데이터 프레임, 구체적으로 티블(tibble)을 반환한다. 티블은 타이디버스(tidyverse) 데이터 프레임의 특수한 유형이다. 열 중 일부는 중첩된 데이터가 있는 목록 열로, glimpse(my_recent_likes를 사용해서 볼 수 있다. 여기서 몇 가지 정보를 추출하고 싶을 수 있다.

bluesky_glimpse_02

IDG

이 이미지에서 볼 수 있듯이 ‘좋아요’가 표시된 각 포스트의 text, author_name, author_handle, like_count, repost_count가 일반(중첩되지 않은) 열에 배열된다. 그러나 포스트가 생성된 타임스탬프는 posted_data 목록 열에 중첩돼 있으며, 포스트에 언급된 모든 URL은 embed_data 열 안에 묻혀 있다.

tidyr 패키지의 unnest_wider()를 사용해서 이러한 열의 중첩을 해제하는 방법 하나는 다음과 같다.

my_recent_likes_cleaned 
unnest_wider(post_data, names_sep = "_", names_repair = "unique") |>
unnest_wider(embed_data, names_sep = "_", names_repair = "unique") |>
unnest_wider(embed_data_external, names_sep = "_", names_repair = "unique")

이렇게 하면 40개의 열이 있는 데이터 프레임(티블)이 생성된다. 원하는 열을 선택해서 이름을 바꾸고 나중에 활용할 수 있도록 TimePulled 타임스탬프 열을 추가한다.

my_recent_likes_cleaned   select(Post = text, By = author_handle, Name = author_name, CreatedAt = post_data_createdAt, Likes = like_count, Reposts = repost_count, URI = uri, ExternalURL = embed_data_external_uri) |>  mutate(TimePulled = Sys.time() )

‘좋아요’가 표시된 각 포스트의 URL이 포함된 열도 있다면 유용할 것이다. 기존 URI 열은 at://did:plc:oky5czdrnfjpqslsw2a5iclo/app.bsky.feed.post/3lbd2ee2qvm2r과 같은 형태로 돼 있다. 그러나 포스트 URL은 https://bsky.app/profile/{author_handle}/post/{a portion of the URI} 구문을 사용한다.

다음의 R 코드는 URI와 By 열을 사용해 URL 열을 생성하고 URL에 사용되는 URI 부분을 위한 새 PostID 열을 추가한다.

my_recent_likes_cleaned mutate(PostID = stringr::str_replace(URI, "at.*?post\/(.*?)$", "\1"),       URL = glue::glue("https://bsky.app/profile/{By}/post/{PostID}") )

필자는 일반적으로 이와 같은 데이터를 rio 패키지를 사용해 파케이(Parquet) 파일로 저장한다.

rio::export(my_recent_likes_cleaned, "my_likes.parquet")

이렇게 파일 이름에서 파일 확장자를 바꾸는 간단한 방법으로 .rds 파일 또는 .csv 파일 등 다양한 형식으로 데이터를 저장할 수 있다.

3단계. 블루스카이 ‘좋아요’ 파일을 업데이트된 상태로 유지

‘좋아요’ 컬렉션을 한 시점의 스냅샷으로 보관하는 것도 좋지만 유용성을 높이려면 계속 업데이트해야 한다. 더 고급스러운 다른 방법도 있겠지만 일단 간단한 방법은 오래된 데이터를 로드하고 새로운 데이터를 가져온 다음 오래된 데이터 프레임에 없는 행을 찾아서 오래된 데이터를 추가하는 방법이다.

previous_my_likes 

그 다음 위에 나온, ‘좋아요’ 저장까지의 모든 코드를 실행해서 최근의 ‘좋아요’를 가져온다. 각자의 ‘좋아요’ 활동 수준에 맞춰 한도를 변경한다.

my_recent_likes   unnest_wider(post_data, names_sep = "_", names_repair = "unique") |>  unnest_wider(embed_data, names_sep = "_", names_repair = "unique") |>  unnest_wider(embed_data_external, names_sep = "_", names_repair = "unique") |>  select(Post = text, By = author_handle, Name = author_name, CreatedAt = post_data_createdAt, Likes = like_count, Reposts = repost_count, URI = uri, ExternalURL = embed_data_external_uri) |>  mutate(TimePulled = Sys.time() ) |>  mutate(PostID = stringr::str_replace(URI, "at.*?post\/(.*?)$", "\1"),       URL = glue::glue("https://bsky.app/profile/{By}/post/{PostID}") )

기존 데이터에 이미 존재하지 않는 새로운 ‘좋아요’를 찾는다.

new_my_likes 

새 데이터와 오래된 데이터를 결합한다.

deduped_my_likes 

마지막으로, 오래된 파일을 덮어써서 업데이트된 데이터를 저장한다.

rio::export(deduped_my_likes, 'my_likes.parquet')

4단계. 편리한 방식으로 데이터를 보고 검색

이 데이터를 검색 가능한 테이블에서 사용할 수 있도록 만들고자 한다. 각 포스트 텍스트의 끝에는 블루스카이의 원본 포스트로 연결되는 링크가 포함돼 있어 포스트의 일반 텍스트에는 없는 모든 이미지와 댓글, 상위 항목 또는 스레드를 손쉽게 볼 수 있다. 또한 테이블에서 필요 없는 일부 열을 제거한다.

my_likes_for_table    mutate(     Post = str_glue("{Post} >>"),     ExternalURL = ifelse(!is.na(ExternalURL), str_glue("{substr(ExternalURL, 1, 25)}..."), "")         ) |>  select(Post, Name, CreatedAt, ExternalURL)

다음은 DT 패키지를 사용해서 이 데이터의 검색 가능한 HTML 테이블을 만드는 방법이다.

DT::datatable(my_likes_for_table, rownames = FALSE, filter = 'top', escape = FALSE, options = list(pageLength = 25, autoWidth = TRUE, filter = "top", lengthMenu = c(25, 50, 75, 100), searchHighlight = TRUE,                  search = list(regex = TRUE)                                                                                 ))

이 테이블의 오른쪽 상단에는 테이블 전체 검색 상자와 각 열에 대한 검색 필터가 있어서 테이블에서 두 개의 검색어를 검색할 수 있다. 예를 들어 주 검색창에서 #rstats 해시태그를 검색한 다음, 포스트 열 필터창에서 LLM이 포함된 포스트를 검색할 수 있다(테이블 검색은 대소문자를 구분하지 않음). 또는 search = list(regex = TRUE)옵션으로 정규식 검색이 활성화되어 있으므로 검색 상자에 (?=.rstats)(?=.(LLM)) 전방탐색 패턴 하나만 사용할 수도 있다.

bluesky_table_03

IDG

챗GPT, 클로드 같은 생성형 AI 챗봇은 복잡한 정규식을 아주 잘 작성할 수 있다. 테이블에서 일치하는 텍스트 하이라이트를 켜두면 정규식이 원하는 대로 작동하는지 여부를 쉽게 확인할 수 있다.

LLM으로 블루스카이 ‘좋아요’ 쿼리하기

생성형 AI를 사용해서 이러한 포스트를 쿼리하는 가장 간단하면서 무료인 방법은 원하는 서비스에 데이터 파일을 업로드하는 것이다. 필자의 경우 구글 노트북LM에서 좋은 결과를 얻었다. 노트북LM은 무료이며 답변에 사용한 소스 텍스트를 보여준다. 파일 제한도 넉넉해서 소스당 50만 단어 또는 200MB다. 또한 구글은 LLM 학습에 사용자의 데이터를 사용하지 않는다고 한다.

“과학 관련 컬러 팔레트가 있는 R 패키지에 대한 이야기”라는 쿼리는 필자가 예전에 ‘좋아요’를 누르고 다시 포스트했던 항목을 정확히 찾아서 가져왔다. 노트북LLM에 1) 답변에 해당 문서만 사용하고 2) 응답을 생성하는 데 사용한 소스 텍스트를 보고싶다는 것을 알려주기 위해 프롬프트를 작성해 제공하거나 지시를 내릴 필요가 없었다. 그냥 질문한 것이 전부다.

bluesky_noteboklm_04a

IDG

데이터의 유용성을 높이고 낭비를 줄이기 위해 CreatedAt를 시간을 제외한 날짜로 제한하고, 포스트 URL을 별도의 열에 유지하고(HTML이 추가된 클릭 가능한 링크 대신) 외부 URL 열을 삭제했다. 이렇게 간결하게 정리한 버전을 .csv 파일이 아닌 .txt 파일로 저장했다. 노트북LM은 .csv 확장자를 처리하지 못하기 때문이다.

my_likes_for_ai    mutate(CreatedAt = substr(CreatedAt, 1, 10)) |>  select(Post, Name, CreatedAt, URL)rio::export(my_likes_for_ai, "my_likes_for_ai.txt")

노트북LM에 ‘좋아요’ 파일을 업로드한 후 파일이 처리되면 곧바로 질문을 시작할 수 있다.

bluesky_noteboklm_04

IDG

외부 서비스를 사용하지 않고 R 내에서 문서를 쿼리하고자 한다면 한 가지 방법은 깃허브 프로젝트인 엘머 어시스턴트(Elmer Assistant)를 사용하는 것이다. 프롬프트와 소스 정보를 필요에 맞게 간단히 수정할 수 있을 것이다. 다만 상당한 성능의 윈도우 PC에서도 로컬로 엘머 어시스턴트를 실행하기는 어려웠다.

자동 실행 스크립트를 예약해 ‘좋아요’ 업데이트하기

이 작업이 쓸모가 있으려면 “내가 ‘좋아요’를 표시한 포스트” 데이터를 최신 상태로 유지해야 한다. 필자의 경우 블루스카이를 사용할 때 로컬 컴퓨터에서 수동으로 스크립트를 실행하지만 매일 또는 매주 자동으로 실행되도록 스크립트를 예약할 수도 있다. 다음은 세 가지 방법이다.

  • 로컬에서 스크립트 실행. 항상정확한 일정에 따라 스크립트가 실행된다는 점에 대해 그다지 걱정하지 않는다면 윈도우용 taskscheduleR, 또는 맥이나 리눅스용 cronR 같은 툴을 사용해서 자동으로 R 스크립트를 실행할 수 있다.
  • 깃허브 액션 사용. atrrr 패키지를 만든 요하네스 그루버는 무료 깃허브 액션을 사용해서 R 블로거 블루스카이 봇을 실행하는 방법을 설명한다. 이 지침을 다른 R 스크립트에 맞게 수정할 수 있다.
  • 클라우드 서버에서 스크립트 실행하기. 디지털 오션(Digital Ocean)과 같은 퍼블릭 클라우드의 인스턴스와 크론 작업을 함께 사용하는 방법도 있다.

스프레드시트 또는 R 툴을 사용해서 북마크와 메모 열 추가하기

블루스카이 ‘좋아요’ 데이터가 필요하지만 지금까지 내가 ‘좋아요’를 누른 모든 포스트가 포함되는 것은 원하지 않을 수 있다. 단순히 작성자를 격려하기 위해 ‘좋아요’를 누르거나, 또는 포스트가 재미있어서 ‘좋아요’를 클릭했지만 나중에 다시 찾을 일은 없는 경우가 그렇다.

주의할 점이 있다. 많은 포스트에 ‘좋아요’를 누르는 사람이 데이터를 최신 상태로 유지하고자 하는 경우 스프레드시트에서 일일이 수동으로 북마크를 표시하기가 고될 수 있다는 점이다. “북마크” 하위 집합을 선별해 만드는 대신 ‘좋아요’ 데이터베이스 전체를 검색하는 방법을 사용해도 된다.

필자가 사용하고 있는 프로세스는 다음과 같다. 초기 설정에서는 엑셀 또는 .csv 파일을 사용할 것을 권한다.

1단계. ‘좋아요’를 스프레드시트로 가져오고 열 추가

먼저 my_likes.parquet 파일을 가져오고 빈 북마크와 메모 열을 추가한 다음 새 파일로 저장한다.

my_likes   mutate(Notes = as.character(""), .before = 1) |>  mutate(Bookmark = as.character(""), .after = Bookmark)rio::export(likes_w_bookmarks, "likes_w_bookmarks.xlsx")

필자의 경우 얼마간 실험한 후에 문자로 북마크 열을 만들어서 논리 TRUE나 FLASE 열이 아닌 “T” 또는 “F”를 스프레드시트에 추가할 수 있도록 했다. 문자를 사용하면 이 데이터를 R 외부에서 사용할 때 R의 부울 필드가 적절히 변환될지 여부에 대해 신경 쓸 필요가 없다. 메모 열에는 무언가를 다시 찾을 이유를 설명하는 텍스트를 추가할 수 있다.

다음 단계는 이 프로세스의 수동 부분으로, 북마크로 유지할 ‘좋아요’에 표시하는 것이다. 스프레드시트에서 열면 F나 T를 클릭해서 동시에 여러 셀에 드래그할 수 있으므로 편리하다. ‘좋아요’ 수가 많다면 지루한 작업이 될 것이다. 지금은 일단 모두 “F”로 표시하고 앞으로 수동으로 북마크하는 방법을 사용하면 부담을 덜 수 있다.

파일을 likes_w_bookmarks.xlsx로 수동으로 다시 저장한다.

2단계. 스프레드시트와 ‘좋아요’의 동기화 유지

이 초기 설정이 끝난 다음에는 데이터가 업데이트됨에 따라 스프레드시트가 데이터와 동기화되도록 하는 것이 좋다. 이를 구현하는 방법 중 하나는 다음과 같다.

새 deduped_my_likes ‘좋아요’ 파일을 업데이트한 다음 북마크 확인 조회를 생성한 뒤 중복 제거된 ‘좋아요’ 파일과 결합한다.

bookmark_check   select(URL, Bookmark, Notes)my_likes_w_bookmarks   relocate(Bookmark, Notes)

새로운 ‘좋아요’ 데이터와 기존 북마크 데이터가 결합된 파일이 생성되며 아직 상단에는 북마크 또는 메모 항목이 없다. 스프레드시트 파일로 저장한다.

rio::export(my_likes_w_bookmarks, "likes_w_bookmarks.xlsx")

수작업이 포함되고 다소 귀찮은 이 프로세스의 대안은 중복 제거된 ‘좋아요’ 데이터 프레임에 dplyr::filter()를 사용해서 다시 찾을 일이 없는 항목(예를 들어 좋아하는 스포츠 팀을 언급한 포스트, 또는 그 당시에만 의미가 있었던 특정 날짜에 대한 포스트)을 제거하는 방법이다.

다음 단계

사용자가 직접 쓴 포스트를 검색하고 싶다면 atrrr의 get_skeets_authored_by() 함수를 사용해서 비슷한 워크플로우에 따라 블루스카이 API를 통해 가져올 수 있다. 일단 이 길에 올라서고 나면 훨씬 더 많은 일을 할 수 있음을 알게 되고, 같은 길을 가는 다른 R 사용자도 만나게 될 것이다.
dl-itworldkorea@foundryco.com

관련자료

댓글 0
등록된 댓글이 없습니다.
Member Rank