← DEVLOG
우주 과학2025.12.287 min read

외계행성 — NASA 데이터베이스를 탐색하는 UI 만들기

NASA Exoplanet Archive에서 실제 외계행성 데이터를 가져와 거주 가능성으로 분류하고 Canvas로 시각화한 기록

nasa-apidata-visualizationcanvasfilter

시작 — 5,000개가 넘는 실제 외계행성

2022년 NASA가 확인된 외계행성 수가 5,000개를 넘었다고 발표했습니다. 이 행성들은 실제 관측 데이터로 이루어진 공개 데이터베이스에 저장되어 있습니다.

단순히 목록을 보여주는 것이 아니라, 거주 가능성 기준으로 분류하고 각 행성의 물리적 특성을 시각적으로 탐색할 수 있는 UI를 만들고 싶었습니다.


API — TAP 서비스와 ADQL 쿼리

NASA Exoplanet Archive는 TAP(Table Access Protocol) 서비스를 제공합니다. SQL과 유사한 ADQL 쿼리로 원하는 컬럼만 선택해 가져올 수 있습니다.

const QUERY = `
  SELECT pl_name, pl_rade, pl_bmasse, pl_insol, st_teff,
         pl_orbper, sy_dist, disc_year, discoverymethod
  FROM ps
  WHERE pl_controv_flag = 0
`;

const FETCH_URL =
  `https://exoplanetarchive.ipac.caltech.edu/TAP/sync` + `?query=${encodeURIComponent(QUERY)}&format=json`;

pl_controv_flag = 0 조건은 논란이 있는 후보 행성을 제외하고 확인된 행성만 가져오기 위한 필터입니다. 전체 쿼리 결과는 약 5,500개 행으로, 응답 크기가 상당해서 클라이언트 캐싱이 필수였습니다.


거주 가능성 분류

거주 가능성의 핵심 지표는 일사량(insolation flux, pl_insol)입니다. 단위는 지구 기준(Earth = 1)이며, 지구와 유사한 일사량 범위에 있는 행성을 거주 가능 후보로 분류합니다.

type Habitability = 'habitable' | 'hot' | 'cold' | 'unknown';

function getHabitability(p: Planet): Habitability {
  if (p.pl_insol == null) return 'unknown';
  if (p.pl_insol >= 0.25 && p.pl_insol <= 1.77) return 'habitable';
  if (p.pl_insol > 1.77) return 'hot';
  return 'cold';
}

0.25~1.77 범위는 Kopparapu et al. (2013)의 낙관적 거주 가능 구역(Optimistic HZ) 기준을 따랐습니다. 다만 일사량만으로는 진정한 거주 가능성을 판단할 수 없으며, 이 분류는 탐색의 시작점에 가깝습니다.


Canvas 시각화 — 크기와 온도로 표현

행성 목록을 단순 텍스트로 나열하는 대신, Canvas에 각 행성을 원으로 시각화했습니다.

  • 원의 크기: 행성 반경(pl_rade, 지구 반경 기준) — 로그 스케일 적용
  • 원의 색상: 항성 유효 온도(st_teff, K) — 차갑고 붉은 별(2,500K)에서 뜨거운 파란 별(30,000K)까지
function getTempColor(teff: number | null): string {
  if (teff == null) return 'rgba(150, 150, 150, 0.7)';
  // 2500K=붉은 적색왜성, 5778K=태양, 10000K=청백색
  const t = Math.max(0, Math.min(1, (teff - 2500) / 10000));
  const r = Math.round(255 * (1 - t * 0.6));
  const g = Math.round(180 * t);
  const b = Math.round(80 + 175 * t);
  return `rgba(${r}, ${g}, ${b}, 0.85)`;
}

거주 가능 행성으로 분류된 것들은 shadowBlur로 초록색 글로우를 추가해 강조했습니다.


필터와 정렬

탐색을 돕기 위한 필터 시스템을 구현했습니다.

  • 타입 필터: All / Habitable Zone / Earth-like (반경 0.5~1.5) / Giant (반경 > 6)
  • 발견 방법: Transit / Radial Velocity / Direct Imaging / 기타
  • 발견 연도: 슬라이더로 범위 지정

필터 결과 건수를 실시간으로 표시하고, 거주 가능 행성이 필터 결과에 포함되면 상단에 별도로 목록을 노출합니다.


클라이언트 캐시와 AbortController

데이터는 한 번 로드하면 exoplanet_data_v2 키로 localStorage에 저장합니다.

const abortRef = useRef<AbortController | null>(null);

async function fetchExoplanets() {
  abortRef.current?.abort();
  abortRef.current = new AbortController();

  const cached = storageGet<Planet[] | null>('exoplanet_data_v2', null);
  if (cached) {
    setData(cached);
    return;
  }

  const res = await fetch(FETCH_URL, { signal: abortRef.current.signal });
  const json = await res.json();
  const planets = parseRows(json);
  storageSet('exoplanet_data_v2', planets);
  setData(planets);
}

컴포넌트 언마운트 또는 재요청 시 이전 요청을 AbortController로 취소해 경쟁 상태(race condition)를 방지했습니다.


캔버스 성능 — 4K 해상도 캡

수천 개의 행성을 Canvas에 동시에 렌더링하면 픽셀 처리량이 상당합니다. 특히 거주 가능 행성에 적용되는 shadowBlur 글로우 효과는 픽셀 수에 비례해 비용이 증가합니다. 4K 디스플레이에서 캔버스 버퍼가 과도하게 커지는 것을 방지하기 위해, 총 픽셀 수를 1920 × 1080 기준으로 제한하는 캡을 적용했습니다.

5,000개 이상의 행성 점을 동시에 그리는 페이지인 만큼, 이 캡의 효과가 특히 체감되었습니다.


마치며

행성 목록을 처음 렌더링했을 때, 수천 개의 점이 Canvas에 펼쳐지는 것을 보며 잠시 멈췄습니다. 이것들이 실제로 존재하는 행성들이라는 사실 — 누군가의 관측이 데이터로 쌓이고, 그것이 이 화면 위 점 하나로 나타난다는 감각은 목록을 스크롤하는 것과는 전혀 다른 경험이었습니다.

이 포스트와 연결된 콘텐츠

직접 체험하기