Immersive Course(이하 IM)는 프로그래밍 교육기관 코드 스테이츠의 웹 개발 심화 코스이다. 아래 내용은 IM에서 배우고, 내가 찾아보고, 다른 수강생들이 전해 준 지식이다.
폭풍 같은 일주일이 지나갔다. 조급한 마음을 평온하게 다스리고 싶지만 다스릴 수 없었던 한 주, 매일 코딩하는 꿈을 꾼 탓에 어쩐지 잠을 자도 의자에 앉아있던 것 같은 기분이 든 한 주였다. 팀원 모두가 도구를 새로 익히고 익숙해지는데 시간을 많이 투자했다.
React Native로 개발을 하면서 expo를 사용하지 않기로 했으므로 ios 개발을 하려면 xcode가 가동되는 환경이 필요했는데, 프론트엔드 팀원이 우분투와 안드로이드 핸드폰을 사용 중이라 ios 테스트가 불가능했다. 그래서 우선 안드로이드 버전만 먼저 개발 후 배포하기로 했다.
프론트엔드에서는 TypeScript 도입을 미루기로 했다. 기본 개념은 이해했지만, 그를 React Native와 React Navigation에 적용하는 방법을 찾는 데 예상보다 긴 시간을 들였다. 회의 끝에 남은 시간을 고려하여 데드라인까지 구현할 코드에는 도입하지 않기로 했다.
최초 설계 시에는 눈치채지 못했는데, 작업을 진행하다 보니 많은 경우 '새로운 내용 등록'과 '기존 내용 수정' 컴포넌트의 모양과 구조가 같음을 깨달았다. 등록과 수정 모두 정보를 인풋에 입력하고 확인 버튼을 누르는 방식으로, 인풋의 value값과 버튼에 표시될 글자, 버튼을 눌렀을 때 실행될 함수 등을 제외하고 많은 부분이 같았다. 그래서 따로 나눠놓았던 식물 관리 기록 등록과 수정 컴포넌트를 하나로 만들었다. 이외에도 구조가 비슷하지만 합치지 못한 부분이 있어 차후에 고치려고 메모를 해두었다.
앱에서 사용하는 폰트를 Noto Sans KR로 지정했다. 그런데 폰트 위아래에 여분의 여백이 존재하여 레이아웃 설정 시 문제가 발생했다. 구글에서 여러 가지 해결법을 찾아 적용해보았으나 고쳐지지 않았다. 안드로이드 스튜디오에서 작업하고 네이티브 언어로 작성하는 경우에는 아래 두 속성을 지정하여 해결되는 것 같다.
includeFontPadding: false,
textAlignVertical: 'center'
그래서 Noto Sans와 Ratto를 베이스로 만든 글꼴 Spoqa Han Sans로 웹폰트를 변경했다. Spoqa Han Sans는 여백 문제가 없다.
저번 프로젝트와는 다르게, 이번에는 백엔드에서 일부 API 요청을 완성했더라도 먼저 적용하지 않고 모든 데이터를 우선 가짜 데이터로 채워 테스트했다. API 요청으로 돌아오는 데이터를 받기까지의 속도가 상대적으로 가짜 데이터를 읽는 시간보다 느린 탓에 기능 확인을 제대로 할 수 없는 경우가 있었기 때문이다.
그래서 우선 가짜 데이터로 데이터 흐름을 모두 잡은 후, 실제 완성된 API 요청을 차례로 적용했다.
API 요청을 한 곳에 모아 모듈로 만든 후 필요한 컴포넌트에서 그를 불러와 사용하는 형태를 만들어보았다. 이전 프로젝트에서는 컴포넌트마다 따로 데이터 요청을 작성했는데, URL이나 헤더 옵션을 수정할 때 모든 요청을 일일이 찾아 바꿔야 했던 경험이 있어 수정과 관리가 용이하도록 했다. 더불어 기기에서 갤러리와 카메라에 접근해 사진을 고를 수 있도록 만드는 함수, 이미지를 서버로 보내기 위해 body를 formdata 형식으로 변경해주는 함수를 모듈화하여 필요한 곳에서 불러와 사용했다.
import axios from 'axios';
import { baseUrl, headers } from '..';
import { formDataMaker } from '../../helper';
axios.defaults.withCredentials = true;
const endpoint = `${baseUrl}/plant`;
const Plant = {
// 식물을 새로 추가하는 요청
addPlant: async (image, bodyData) => {
const body = formDataMaker(image, bodyData);
try {
const response = await axios.post(endpoint, body, headers);
return response;
} catch (error) {
console.error(error);
return error.message;
}
},
getPlantData: async plantId => {
// 입력된 식물 데이터를 받는 요청
try {
const response = await axios.get(`${endpoint}/${plantId}`, headers);
response.data.adoptionDate = response.data.adoptionDate.slice(0, 10);
return response;
} catch (error) {
console.error(error);
return error.message;
}
},
// ...그 외 요청들...
};
export default Plant;
실제로 화면을 만들어나가다 보니, 기획 단계에서 세운 User Flow에서 미처 생각하지 못한 부분들을 발견하여 덧붙이고 수정해가며 진행했다.
UI 라이브러리에서 매서드를 이용해 전체 테마를 지정할 수 있었는데, 그 테마 객체는 UI라이브러리에 속한 컴포넌트에서만 props로 받아 사용할 수 있었다. 라이브러리에 속하지 않은 부분들에도 동일한 테마(컬러)를 사용하기 위해서 기존에 최상위 컴포넌트 안에서 테마를 적용하던 구조를 theme파일을 따로 만들어 구분했다.
// theme.js
import { DefaultTheme } from 'react-native-paper';
const theme = {
...DefaultTheme,
// 필수적인 키 색상들을 설정. 여기에 원하는 다른 컬러들을 추가해 지정할 수 있다
// e.g. subPrimary: '#aaa'
colors: {
...DefaultTheme.colors,
primary: '#79B38C',
accent: '#F27C7C',
text: '#333333',
onBackground: '#333333',
onSurface: '#333333',
background: '#FFFFFF',
surface: '#f6f6f6',
},
};
// App.js와 기타 원하는 컴포넌트에서 theme를 import하여 사용한다.
export default theme;
스크롤이 필요한 스크린에서, 스크롤로 화면을 움직이더라도 같은 자리에 위치한 버튼을 구현했다. 이 때 모바일에서는 스크롤이 필요한 화면은 <ScrollView>
컴포넌트로 묶고, 그와 같은 레벨에 띄우고자 하는 버튼을 위치시킨 후 다시 한 번 <View>
로 묶어준다. 이 때 버튼 컴포넌트를 <ScrollView>
의 코드 상 아래로 위치시켜야만 버튼 터치가 가능하다.
// 엘리먼트 구조 부분
return (
// 이 View를 기준으로 Button 컴포넌트의 위치를 정한다
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.scrollView}>
{/* 엘리먼트들 */}
</ScrollView>
{/* ScrollView 보다 아래에 코드가 위치해야만 버튼 터치가 가능하다 */}
<View style={styles.button}>
<Button/>
</View>
</View>
);
// 컴포넌트 스타일 부분
import { ScrollView, View, StyleSheet, Dimensions } from 'react-native';
// ScrollView 가로 너비를 확정하기 위해 모바일 기기 화면 수치를 얻는다
const { width } = Dimensions.get('window');
const styles = StyleSheet.create({
container: {
alignItems: 'center',
},
scrollView: {
width,
},
button: {
position: 'absolute',
bottom: 30,
padding: 5,
},
});
팀에서 이따금 어떤 기능 하나를 너무 완성도 높게 구현하려다 진도가 나가지 않는 상황이 생겼는데, 시간이 없을 때 한 가지 기능을 구현하면서 너무 높은 기준을 세우면 스스로가 압박을 크게 받으면서 진행도 어려워지고 받는 스트레스도 커졌다. 우선 기능 전체를 가볍게 한 바퀴 돌면서 얕게 구성하고, 파이 결을 늘려가듯이 그 위에 다시 한번, 다시 또 한 번 기능을 추가하고 다듬는 방식이 완성에 빠르게 가까워지는 방식이라는 것을 느꼈고 팀과 이야기를 나누었다.
발등에 불을 가만히 진정시키면서 나아가야 할 기간이 일주일 남았다. 본격적으로 실제 API를 테스트하면서 동시에 스타일링을 진행하려니 정신없지만, 그림으로만 그렸던 화면이 실제 움직임을 더해 나타나는 모습을 보는 일은 정말 신난다. 시간 부족으로 포기한 부분이 생겼지만, 서비스가 하나의 묶음으로 온전하게 마감될 수 있도록 발표일 전까지 달려야겠다.