안드로이드 스튜디오 설치와 버추얼 디바이스
교재와 현재간의 개발환경 차이가 컸다. 기본 설치 후 가상 디바이스(AVD)를 띄우는 데에도 여러 난관에 봉착했었다. 비슷한 문제를 겪은 다른 한국인 개발자들의 글들을 참고하며, The emulator process for AVD has terminated 와 같은 문제들을 해결해나갔다.
아래는 내가 취한 방법들이다.
1. API 버전 낮추기
2. 에뮬레이터 데이터 초기화 (Wipe Data)
3. 에뮬레이터 렌더링 모드 변경 (AVD 설정에서 렌더링 모드를 "Software - GLES 2.0"으로 변경)
이 외에 다른 분들의 경우에는 ANDROID_HOME 환경변수와 SDK쪽 문제를 해결하기도 했다.
리액트 네이티브
RN 코드 예제
{/* RN의 text태그는 html의 span
태그와 동일한 기능을 합니다. */}
{/* RN에서 이미지 컴포넌트는 HTML의 이미지와 동일기능 */}
{/* 네트워크 or 로컬 환경의 이미지 출력 */}
{/* source ={{uri:"github.com/xinker-kim.png"}} */}
{/* RN에서 TextInput 컴포넌트는 HTML의 인풋태그에 해당. */}
{/* 사용자의 입력을 받을 수 있는 컴포. */}
{/* RN에서 Button || Pressable 컴포넌트 버튼 */}
{/* RN에서 safeAreaView 컴포넌트는 기기별 "안전한 영역(safe area)"을 */}
{/* 고려하여 콘텐츠를 배치하도록 보장합니다. */}
{/* SafeAreaView는 기기의 화면 중 콘텐츠가 시스템UI에 가려지지않도록 보호해주는 레이아웃 컴포넌트입니다. */}
<Modal
visible={true}
animationType="slide"
presentationStyle="fullScreen"
>
<SafeAreaView>
<Text>모달창 화면입니다.</Text>
<Pressable onPress={() => alert("안녕하세요")}>
<Text>모달창을 닫습니다.</Text>
</Pressable>
</SafeAreaView>
</Modal>
SafeAreaView, Slot, Stack같은 생소한 개념들이 어려웠다.
포켓몬API 리액트 네이티브 앱
리액트 네이티브 환경에서 포켓몬 API를 활용한 앱을 만들어보기로 했다.
PokéAPI
Try it now! Need a hint? Try pokemon/ditto, pokemon-species/aegislash, type/3, ability/battle-armor, or pokemon?limit=100000&offset=0. Direct link to results: https://pokeapi.co/api/v2/pokemon/ditto Resource for ditto { "abilities": [ { "ability": { "name"
pokeapi.co
API는 위의 사이트에서 제공된다.
https://github.com/XinKer-Kim/RN-pokemon-catch
GitHub - XinKer-Kim/RN-pokemon-catch: 리액트 네이티브로 만드는 포켓몬 캐치
리액트 네이티브로 만드는 포켓몬 캐치. Contribute to XinKer-Kim/RN-pokemon-catch development by creating an account on GitHub.
github.com
개발 결과물은 해당 깃허브에 업로드되고, 개발 일지는 별도로 작성해볼 생각이다.
초기 세팅 설치에 관한 내용은 다음과 같다. (디스코드 스레드에 업로드 함)
초기 설치
[https://docs.expo.dev/]
npx create-expo-app@latest
npm run reset-project
npx expo install expo-router react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar
(선택 npx expo install react-native-web react-dom)
[https://www.nativewind.dev/]
npx expo install nativewind react-native-reanimated@~3.17.4 react-native-safe-area-context@5.4.0
npx expo install --dev tailwindcss@^3.4.17 prettier-plugin-tailwindcss@^0.5.11
npx tailwindcss init
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
// NOTE: Update this to include the paths to all files that contain Nativewind classes.
content: ["./app/**/*.{js,jsx,ts,tsx}","./src/**/*.{js,jsx,ts,tsx}"],
presets: [require("nativewind/preset")],
theme: {
extend: {},
},
plugins: [],
};
global.css
@tailwind base;
@tailwind components;
@tailwind utilities;
babel.config.js
module.exports = function (api) {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
};
};
const { getDefaultConfig } = require("expo/metro-config");
const { withNativeWind } = require('nativewind/metro');
const config = getDefaultConfig(__dirname)
module.exports = withNativeWind(config, { input: './app/global.css' })
일련의 과정들을 마치면 expo를 통해 AVD에서 RN앱을 볼 수 있다.
Grid 대신 FlatList
홈 화면에 2열짜리 grid와 gap을 주려고 했는데, 리액트 네이티브에서는 지원이 되지 않았다. flex에서 갭을 쓰면 w-[48%]같은 짜치는 값을 써야한다. 그 대신 플랫리스트를 활용하면, numColumns 속성으로 컨트롤 할 수 있다!
<FlatList
data={pokemonList}
numColumns={2}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <PokemonCard pokemonId={item.id} />}
/>
2개의 열을 줌으로써 괜찮은 배치가 되었다.
expo-font 사용
리액트 네이티브에서는 웹폰트 사용을 할 수가 없었다.
ttf로 다운로드 받고, expo-font를 이용해 불러와서 사용하면 된다.
font-face를 정의하지 않고도 이런식으로 쓸 수 있었다.
API 동시 호출
영문 데이터와 이미지 url은 기본 API에 있지만, 한글 텍스트는 별도의 API를 호출해야한다. 이를 동시에 받기 위해 Promise를 사용했다.
한글로 된 데이터는 pokemon-species에 들어있다. pokemonId를 프롭으로 전달해주면, 오른쪽과 같이 보여줄 수 있다.
포획 모달 구현
최초의 구현은 포켓몬 카드 클릭시 모달컴포넌트를 띄우고, 스프라이트와 포획버튼 및 닫기버튼을 만들어주는 방식이었다.
import { useModalStore } from "@/src/store/modalStore";
import { Image, Modal, Pressable, Text, View } from "react-native";
export function PokemonDetailModal() {
// 1. 전역 스토어에서 상태와 액션을 가져옵니다.
const { isVisible, pokemonData, closeModal } = useModalStore();
// 2. 보여줄 데이터가 없으면 아무것도 렌더링하지 않습니다.
if (!pokemonData) {
return null;
}
return (
<Modal visible={isVisible} animationType="slide" transparent={true}>
<View className="flex-1 justify-center items-center bg-black/50">
<View className="bg-white w-11/12 rounded-2xl p-4 items-center shadow-lg">
{/* 포켓몬 이미지 */}
<Image
source={{ uri: pokemonData.spriteUrl }}
className="w-48 h-48 -mt-20"
resizeMode="contain"
/>
{/* 포켓몬 이름 */}
<Text
className="text-3xl font-bold mt-2"
style={{ fontFamily: "DungGeunMo" }}
>
{pokemonData.koreanName}
</Text>
{/* 포획, 닫기 버튼 */}
<View className="flex-row w-full mt-6">
<Pressable className="flex-1 bg-red-500 py-3 rounded-lg mr-2">
<Text className="text-white text-center text-lg font-bold">
포획
</Text>
</Pressable>
<Pressable
onPress={closeModal} // 3. 닫기 버튼에 스토어의 closeModal 액션을 연결
className="flex-1 bg-gray-300 py-3 rounded-lg ml-2"
>
<Text className="text-black text-center text-lg font-bold">
닫기
</Text>
</Pressable>
</View>
</View>
</View>
</Modal>
);
}
카드 컴포넌트에 모달을 종속시킬 경우, 카드 개수만큼 모달이 생기기때문에 모달은 전역에서 1개만 사용할 필요가 있었다. 따라서, PokemonDetailModal 컴포넌트를 분리하여 홈페이지에서 호출하기로 했다.
도감 UI 구현
도감페이지는 /(tabs)/pokedex 내부에 구현했다. 151마리의 스프라이트를 호출하고, 잡은 인덱스가 아니라면 밝기를 0%로 하여 검은 실루엣으로 보이게 구현했다.
포획 기능 구현
홈 화면에서 1세대 포켓몬을 랜덤하게 등장시키고, 포획 시도를 할 수 있게 만들었다.
모달창에 게임 텍스트를 띄우고, 조작 가능한 버튼 UI들을 구현했다.
포획 성공과 실패, 성공시 창 닫기도 구현했다.
포획률 계산 코드는 매우 단순화 시켰다. 포획시도값은 기본 포획률 / 255에 포획률 모디파이어를 곱한다. 이후 0~1 사이의 랜덤 숫자와 대소비교를 하여, 크면 포획 성공이다.
포획 시뮬레이션
약간의 스프라이트 애니메이션을 구현했고, 외부 이미지를 불러와서 심심한 배경을 채웠다. 그덕에 제법 게임 같아졌다.
도감 연동 구현
새로운 포켓몬을 잡게되면, Store에 추가되고, 도감에 잡은 포켓몬으로 추가된다.
도감 상세 구현
이름과 번호, 정면 이미지, 포켓몬 종류, 키, 몸무게, 도감설명 등을 표시해줬다. 생각보다 채울 공간이 많아서, 종족값을 추가했다.
시각적인 요소를 더 추가하고싶어서 차트를 넣었다. 육각형 차트를 그리는 데에는 gemini의 도움을 받았다.
이제 도감이 제공하는 정보량이 충분해졌다.
최종 시연
포켓몬에게 먹이/진흙을 던지고, 포켓몬이 도망을 치고, 다른 포켓몬을 포획하여 도감에서 확인하는 모습까지 구현했다. 20개씩 불러오는 무한스크롤도 함께 구현했다.
본 후기는 [한글과컴퓨터x한국생산성본부x스나이퍼팩토리] 한컴 AI 아카데미 2기 (B-log) 리뷰로 작성 되었습니다.
'한컴AI 2기' 카테고리의 다른 글
SQL 공부 1 : SELECT, DISTINCT, CONCAT (0) | 2025.08.21 |
---|---|
[스나이퍼팩토리] 한컴AI 2기 - SQL 쿼리 실습 (3) | 2025.08.13 |
한컴AI 2기[스나이퍼팩토리] 한컴AI 2기 - AI개발자 교육 5주차 (3) | 2025.07.30 |
한컴AI 2기[스나이퍼팩토리] 한컴AI 2기 - AI개발자 교육 4주차 (2) | 2025.07.24 |
한컴AI 2기[스나이퍼팩토리] 한컴AI 2기 - AI개발자 교육 3주차 (2) | 2025.07.18 |