Claude-skill-registry data-skill
본 스킬은 대한민국 공공데이터포털(https://www.data.go.kr/)에서 제공하는 각종 공공데이터 API 를 사용하기 위한 설명입니다. 본 스킬은 공공데이터 개발 또는 공공 API 개발을 할 때 사용하면 됩니다.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/data-skill" ~/.claude/skills/majiayu000-claude-skill-registry-data-skill && rm -rf "$T"
manifest:
skills/data/data-skill/SKILL.mdsource content
대한민국 공공데이터포털 API 스킬
🔥 Center 프로젝트 글 생성/수정/관리 작업 시 필수 확인
⚠️ Center 프로젝트에서 글 관련 작업(생성, 수정, 삭제, 배치 생성 등)을 수행할 때는 반드시 다음 순서로 문서를 확인하세요:
- api-spec.md - API 사용 가이드, 테스트용 토큰, curl 예시
- create_post.sh - 단일/배치 게시글 생성 스크립트
1. 개요
**대한민국 공공데이터포털(data.go.kr)**은 중앙정부, 지자체, 공공기관이 보유한 데이터를 개방·제공하는 공식 플랫폼이다.
주요 특징
- REST API 기반: HTTP GET/POST 방식 호출
- 응답 형식: JSON 또는 XML 선택 가능
- 인증 방식: 서비스키(serviceKey) 기반
- 무료 제공: 대부분의 API는 무료로 사용 가능
2. 실제 API 응답 구조
중요: 공공데이터포털 API 문서와 실제 응답 구조가 다를 수 있다. 아래는 실제 응답 구조이다.
{ "response": { "header": { "resultCode": "0", "resultMsg": "정상" }, "body": { "dataType": "JSON", "items": { "item": [ { /* 실제 데이터 항목 */ } ] }, "numOfRows": 10, "pageNo": 1, "totalCount": 225 } } }
핵심 포인트
| 항목 | 문서 (잘못됨) | 실제 응답 (올바름) |
|---|---|---|
| 결과 코드 | (int) | (string) |
| 데이터 배열 | | |
| 총 개수 | | |
에러 코드
| 코드 | 메시지 | 설명 |
|---|---|---|
| OK | 정상 |
| 시스템 내부 오류 | 서버 에러 |
| 파라미터 부적합 | 요청 파라미터 오류 |
| 등록되지 않은 인증키 | 인증키 미등록 |
| 트래픽 초과 | 일일 호출 횟수 초과 |
| 유효하지 않은 인증키 | 인증키 오류 |
3. 제공 스크립트
3.1 외교부 공지사항 API 테스트 (scripts/test-mofa-api.sh
)
scripts/test-mofa-api.sh외교부 공지사항 API를 터미널에서 직접 테스트할 수 있는 쉘 스크립트이다.
사용법
# 스크립트 실행 ./scripts/test-mofa-api.sh "<URL_ENCODED_API_KEY>" # 예시 ./scripts/test-mofa-api.sh "FAK7%2BJL3rqrFr7Wtn%2FxkKhW8hq1zDsite%2FxQdIwug4pDLD5bsqFJDKzroRXTkY8fm5LXMMMzIaTuvl%2F4iDtQ%2Bw%3D%3D"
출력 내용
- API 요청 정보 (엔드포인트, 파라미터)
- HTTP 상태 코드
- 결과 코드/메시지
- 공지사항 목록 (제목, ID, 작성일)
- 원본 JSON 응답 (처음 1500자)
주의사항
- API Key는 반드시 URL 인코딩된 값을 사용해야 한다
- 공공데이터포털에서 발급받은 키를 그대로 복사하면 이미 인코딩되어 있음
- 스크립트 실행 전 실행 권한 필요:
chmod +x scripts/test-mofa-api.sh
4. 제공 API 레퍼런스
| API | 문서 | 설명 |
|---|---|---|
| 외교부 공지사항 | references/mofa-reminder.md | 출국 전 참고 공지사항 목록 조회 |
4.1 외교부 공지사항 API 빠른 참조
- 서비스 URL:
http://apis.data.go.kr/1262000/NoticeService2/getNoticeList2 - HTTP Method: GET
- 응답 형식: JSON/XML
- 상세 문서: references/mofa-reminder.md
5. Flutter 연동 가이드
Flutter 앱에서 공공데이터 API를 사용하는 방법이다. 본 프로젝트의
lib/services/data/ 디렉토리에 구현되어 있다.
5.1 파일 구조
lib/services/data/ ├── data.service.dart # API 호출 및 캐싱 서비스 (싱글톤) └── mofa_notice.model.dart # 응답 데이터 모델
5.2 모델 클래스 (mofa_notice.model.dart
)
mofa_notice.model.dartMofaNotice - 개별 공지사항
class MofaNotice { final String id; // 공지사항 ID (예: "ATC0000000048200") final String title; // 제목 (HTML 엔티티 포함) final String content; // 내용 (txt_origin_cn) final String writtenDate; // 작성일 (예: "2025-12-15") final String fileUrl; // 첨부파일 URL (없으면 빈 문자열) // HTML 엔티티 디코딩된 제목/내용 반환 String get decodedTitle => _decodeHtmlEntities(title); String get decodedContent => _decodeHtmlEntities(content); }
MofaNoticeResponse - API 응답 래퍼
class MofaNoticeResponse { final String resultCode; // "0" = 성공 final String resultMsg; // 결과 메시지 final int totalCount; // 전체 공지사항 수 final int numOfRows; // 요청한 개수 final int pageNo; // 페이지 번호 final List<MofaNotice> notices; // 공지사항 목록 final DateTime fetchedAt; // 조회 시간 bool get isSuccess => resultCode == '0'; // API 응답 파싱 (실제 구조에 맞게) factory MofaNoticeResponse.fromApiJson(Map<String, dynamic> json) { final response = json['response']; final header = response['header']; final body = response['body']; final items = body['items']['item'] as List; // ... } }
5.3 서비스 클래스 (data.service.dart
)
data.service.dart싱글톤 패턴으로 구현된 API 서비스이다.
핵심 개념
- 싱글톤 패턴: 앱 전체에서 하나의 인스턴스만 사용
- 캐시 우선 전략: 캐시 → API 호출 → 캐시 저장
- 1시간 TTL: 1시간마다 자동으로 캐시 만료
- 이중 캐싱: 메모리 + 파일 캐싱으로 성능 최적화
사용법
import 'package:philgo/services/data/data.service.dart'; // 1. 공지사항 로드 (캐시 우선) final response = await DataService.instance.loadMofaNotices(); // 2. 성공 여부 확인 if (response.isSuccess) { // 3. 공지사항 목록 접근 for (final notice in response.notices) { print('제목: ${notice.decodedTitle}'); print('날짜: ${notice.writtenDate}'); print('내용: ${notice.decodedContent}'); } } // 4. 캐시 강제 초기화 (새로고침) await DataService.instance.clearMofaCache(); // 5. 캐시 남은 시간 확인 final remaining = DataService.instance.mofaCacheRemainingTime;
주요 구현 코드
class DataService { static DataService? _instance; static DataService get instance => _instance ??= DataService._(); // API Key (이미 URL 인코딩됨) static const String apiKey = AppConfig.dataApiKey; // 1시간 캐시 TTL static const Duration _mofaCacheTtl = Duration(hours: 1); // FileCache 사용 (메모리 + 파일 이중 캐싱) late final FileCache<MofaNoticeResponse> _mofaCache = FileCache( cacheName: 'mofa_notices', defaultTtl: _mofaCacheTtl, fromJson: MofaNoticeResponse.fromJson, toJson: (data) => data.toJson(), useMemoryCache: true, ); Future<MofaNoticeResponse> loadMofaNotices() async { // 1. 캐시 확인 final cached = await _mofaCache.get('mofa_notices'); if (cached != null) return cached; // 2. API 호출 final response = await http.get(Uri.parse( '$_mofaApiBaseUrl?serviceKey=$apiKey&returnType=JSON&numOfRows=5&pageNo=1' )); // 3. UTF-8 디코딩 (한글 깨짐 방지) final json = jsonDecode(utf8.decode(response.bodyBytes)); // 4. 실제 응답 구조에 맞게 파싱 final data = MofaNoticeResponse.fromApiJson(json); // 5. 캐시 저장 await _mofaCache.set('mofa_notices', data); return data; } }
5.4 UI 연동 예시
class _NoticeScreenState extends State<NoticeScreen> { bool _isLoading = true; MofaNoticeResponse? _response; @override void initState() { super.initState(); _loadNotices(); } Future<void> _loadNotices() async { final response = await DataService.instance.loadMofaNotices(); setState(() { _response = response; _isLoading = false; }); } @override Widget build(BuildContext context) { if (_isLoading) { return CircularProgressIndicator(); } if (!_response!.isSuccess) { return Text('에러: ${_response!.resultMsg}'); } return ListView.builder( itemCount: _response!.notices.length, itemBuilder: (context, index) { final notice = _response!.notices[index]; return ListTile( title: Text(notice.decodedTitle), subtitle: Text(notice.writtenDate), onTap: () => _showDetail(notice), ); }, ); } }
6. 개발 시 주의사항
- 인증키 보안: 인증키를 소스코드에 직접 하드코딩하지 말고 환경변수나 설정 파일로 관리
- URL Encode: serviceKey는 반드시 URL Encode 처리
- 응답 구조 확인: 문서와 실제 응답 구조가 다를 수 있으므로 반드시 실제 응답 확인
- UTF-8 디코딩: 한글 깨짐 방지를 위해
사용utf8.decode(response.bodyBytes) - HTML 엔티티 디코딩: 제목/내용에
,'
등이 포함될 수 있음 - 캐싱: 동일한 데이터를 반복 조회할 경우 캐싱 적용 권장