Claude Code Plugins

Community-maintained marketplace

Feedback

본 스킬은 대한민국 공공데이터포털(https://www.data.go.kr/)에서 제공하는 각종 공공데이터 API 를 사용하기 위한 설명입니다. 본 스킬은 공공데이터 개발 또는 공공 API 개발을 할 때 사용하면 됩니다.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name data-skill
description 본 스킬은 대한민국 공공데이터포털(https://www.data.go.kr/)에서 제공하는 각종 공공데이터 API 를 사용하기 위한 설명입니다. 본 스킬은 공공데이터 개발 또는 공공 API 개발을 할 때 사용하면 됩니다.

대한민국 공공데이터포털 API 스킬

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
    }
  }
}

핵심 포인트

항목 문서 (잘못됨) 실제 응답 (올바름)
결과 코드 resultCode (int) response.header.resultCode (string)
데이터 배열 data[] response.body.items.item[]
총 개수 totalCount response.body.totalCount

에러 코드

코드 메시지 설명
0 OK 정상
-1 시스템 내부 오류 서버 에러
-2 파라미터 부적합 요청 파라미터 오류
-4 등록되지 않은 인증키 인증키 미등록
-10 트래픽 초과 일일 호출 횟수 초과
-401 유효하지 않은 인증키 인증키 오류

3. 제공 스크립트

3.1 외교부 공지사항 API 테스트 (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"

출력 내용

  1. API 요청 정보 (엔드포인트, 파라미터)
  2. HTTP 상태 코드
  3. 결과 코드/메시지
  4. 공지사항 목록 (제목, ID, 작성일)
  5. 원본 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)

MofaNotice - 개별 공지사항

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)

싱글톤 패턴으로 구현된 API 서비스이다.

핵심 개념

  1. 싱글톤 패턴: 앱 전체에서 하나의 인스턴스만 사용
  2. 캐시 우선 전략: 캐시 → API 호출 → 캐시 저장
  3. 1시간 TTL: 1시간마다 자동으로 캐시 만료
  4. 이중 캐싱: 메모리 + 파일 캐싱으로 성능 최적화

사용법

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. 개발 시 주의사항

  1. 인증키 보안: 인증키를 소스코드에 직접 하드코딩하지 말고 환경변수나 설정 파일로 관리
  2. URL Encode: serviceKey는 반드시 URL Encode 처리
  3. 응답 구조 확인: 문서와 실제 응답 구조가 다를 수 있으므로 반드시 실제 응답 확인
  4. UTF-8 디코딩: 한글 깨짐 방지를 위해 utf8.decode(response.bodyBytes) 사용
  5. HTML 엔티티 디코딩: 제목/내용에 &#39;, &nbsp; 등이 포함될 수 있음
  6. 캐싱: 동일한 데이터를 반복 조회할 경우 캐싱 적용 권장