Slack Events API Auto Collection Rails

Slack Events API로 채널 메시지 자동 수집하기 — Rails 서비스 설계

Slack 봇에 @봇 이관이라고 멘션해야만 메시지가 수집되는 구조였다. 멘토가 매번 봇을 호출하는 게 번거롭다는 피드백이 왔다. “채널에 글이 올라오면 알아서 수집하면 안 되냐?“는 질문에서 시작된 작업 기록이다. 기존 구조: app_mention 기반 기존에는 Slack의 app_mention 이벤트만 구독하고 있었다. def handle_event(event) case event["type"] when "app_mention" handle_mention(event) end end 누군가 @봇 이관 또는 @봇 피드백 홍길동 잘했어요라고 멘션하면 처리되는 구조. 문제는: 멘토가 매번 봇을 불러야 한다 — 피드백을 쓰고 나서 다시 봇을 호출하는 이중 작업 수강생 제출물도 수동 수집 — 과제 채널에 올라온 메시지를 누군가 이관해줘야 함 파일만 올린 경우 놓침 — 텍스트 없이 파일만 공유하면 수집되지 않음 해결: 세 가지 이벤트 추가 구독 Slack 앱 설정에서 Bot Events에 다음을 추가했다: ...

2026-03-12 · 4분 소요 · Seunghan
Slack File To Activestorage Rails Seeds

Slack 파일을 Rails 프로젝트에 반영하기 — URL 저장이 아닌 소스 포함

팀원들이 Slack 채널에 HTML 파일을 과제로 제출하고 있었다. Rails 앱의 제출 상세 페이지에서 이 파일들을 인라인으로 미리보기할 수 있게 만들어야 했다. “URL 저장하면 되겠지"라는 생각으로 시작했다가 세 번의 방향 전환을 거쳤다. 1차 시도: Slack 파일 URL을 그대로 seeds에 저장 Slack의 파일 공유 URL은 이런 형태다: https://slack-files.com/T0xxx-F0xxx-hash 이걸 seeds.rb에 넣고 SlackFileImporter로 다운로드하면 ActiveStorage에 자동 첨부되는 구조가 이미 있었다. SlackFileImporter.new(submission, slack_url).call 문제: SlackFileImporter는 내부적으로 SLACK_BOT_TOKEN 환경변수를 사용한다. 배포 환경에는 토큰이 있지만, seeds가 실행되는 시점에 Slack API 호출이 실패하면 파일이 누락된다. 그리고 근본적으로 slack-files.com URL은 인증 없이 외부 웹에서 접근이 안 된다. ...

2026-03-12 · 4분 소요 · Seunghan

Flutter image_picker 카메라/갤러리 바텀시트 + Riverpod 빈도 기반 카테고리 자동 정렬 삽질기

Flutter로 시민 신고 앱을 만들면서 세 가지 UX 문제를 연달아 만났다. 사진 추가 버튼이 갤러리만 열어서 카메라 촬영이 불가능한 문제 카테고리가 늘어날수록 그리드가 길어져서 스크롤이 많아지는 문제 신고 대상(일반/긴급)이 바뀌어도 버튼 색상이 바뀌지 않아서 직관성이 떨어지는 문제 각각 어떻게 풀었는지 정리한다. 문제 1: image_picker가 갤러리만 열린다 현상 사진 추가 버튼이 pickImage(source: ImageSource.gallery)만 호출해서 카메라로 찍는 게 불가능했다. 앱 자체에 카메라 권한도 있고 NSCameraUsageDescription도 있는데 UI에서 선택지를 아예 안 줬던 것. ...

2026-03-09 · 6분 소요 · Seunghan

Rails 앱을 Hotwire Native로 iOS 앱 만들어 TestFlight 올리기까지의 삽질 기록

Rails 8로 만든 긴급 신고 웹앱 바로신고를 Hotwire Native으로 iOS 앱으로 감싸서 TestFlight에 올리기까지의 과정을 정리합니다. 기술 스택 Backend: Rails 8 + Turbo iOS: Hotwire Native 1.2.2 + XcodeGen 빌드: Makefile 자동화 프로젝트 구조 ios/ ├── project.yml # XcodeGen 설정 ├── ExportOptions.plist # App Store 내보내기 ├── Makefile # 빌드 자동화 └── BaroSingo/ ├── AppDelegate.swift ├── SceneController.swift ├── AppTab.swift ├── Bridge/ │ ├── FormComponent.swift │ ├── HapticComponent.swift │ └── ShareComponent.swift └── Resources/ ├── Assets.xcassets/ └── path-configuration.json 삽질 1: Hotwire Native API 변경 Hotwire.config.userAgent — 읽기 전용 // ❌ 컴파일 에러: 'userAgent' is a get-only property Hotwire.config.userAgent = "BaroSingo iOS" // ✅ 해결: makeCustomWebView 사용 Hotwire.config.makeCustomWebView = { configuration in let webView = WKWebView(frame: .zero, configuration: configuration) webView.customUserAgent = "BaroSingo iOS/1.0 Turbo Native" return webView } Hotwire.loadPathConfiguration — 존재하지 않는 API // ❌ 컴파일 에러: no member 'loadPathConfiguration' Hotwire.loadPathConfiguration(from: [source]) // ✅ 해결: config.pathConfiguration.sources 직접 설정 Hotwire.config.pathConfiguration.sources = [ .file(Bundle.main.url(forResource: "path-configuration", withExtension: "json")!), .server(URL(string: "\(baseURL)/api/hotwire/path-configuration")!) ] Bridge Component에서 ViewController 접근 // ❌ 컴파일 에러: optional type must be unwrapped delegate.webView?.findViewController() // ✅ 해결: delegate?.destination 사용 guard let viewController = delegate?.destination as? UIViewController else { return } 교훈: Hotwire Native는 버전별 API 변경이 잦다. 공식 소스코드와 실제 동작하는 프로젝트를 참고하는 게 가장 확실하다. ...

2026-03-07 · 4분 소요 · Seunghan
개인정보처리방침 이용약관 면책조항 문의