⭐️ Memory/교육수강 회고
위코드 기업협업 회고
개발자 올라프
2022. 7. 26. 04:12
기업 협업 내용
- 식물관리 앱 서비스를 하고 있는 스타트업에서 인턴쉽(`22.06.20 ~ `22.07.15) 진행
- Front-end 4명 참가 (각자 다른 UI 및 기능 구현)
- React Native를 활용한 앱 개발
- Sourcetree를 활용한 Git-flow 경험
- AI 식물 진단을 위한 카메라 UI 및 기능 구현
식물 진단 카메라 시연
식물 진단 카메라 소개
- 사진을 바탕으로 식물을 분석해주는 'AI 식물 진단' API를 사용하기에 앞서 사용자가 사용할 카메라 개발
- 'react-native-vision-camera' 라이브러리를 활용한 카메라 구현
- https://github.com/mrousavy/react-native-vision-camera
- Github 코드를 보며 라이브러리를 사용하는 방법을 터득
상단 내비게이션
- 뒤로 가기 ❌ 버튼은 만들어져 있는 custom-hook 사용
- 플래시 버튼과 화면 전환 버튼은 useState를 활용하여 현재 상태를 지정했다.
현재 상태를 바꾸는 함수인 changeFlash, changeSelfie를 만들어서 해당 플래시 버튼, 화면전환 버튼인 <Pressable>에 적용
const [flash, setFlash] = useState('off');
const [selfie, setSelfie] = useState('back');
const devices = useCameraDevices('wide-angle-camera');
const device = devices[selfie];
const changeFlash = () => {
flash === 'off' ? setFlash('on') : setFlash('off');
};
const changeSelfie = () => {
selfie === 'back' ? setSelfie('front') : setSelfie('back');
};
<Camera
ref={camera}
style={styles.cameraView}
device={device}
isActive={true}
photo={true}
/>
진행 바
- 'react-native-progress' 라이브러리를 활용하여 사용자에게 진행 상황을 알림
- 사용자는 사진 두 장을 촬영해야 하며 현재 촬영된 사진 개수(length)에 따라서 진행 바가 변함
import * as Progress from 'react-native-progress';
<View style={styles.progressView}>
{images.length === 0 ? (
<Progress.Bar progress={0} width={350} color={Color.white} />
) : images.length === 1 ? (
<Progress.Bar progress={0.5} width={350} color={Color.white} />
) : (
<Progress.Bar progress={1} width={350} color={Color.white} />
)}
</View>
사진 추가 및 앨범 기능
- 사용자 기기 앨범에서 사진 가져오기 및 사진 촬영 버튼 기능 구현
- 앨범 가져오기 기능은 'react-native-image-crop-picker' 라이브러리 활용
const [images, setImages] = useState([]);
const nextId = useRef(1);
const getAlbum = () => {
ImagePicker.openPicker({
width: 300,
height: 400,
cropping: true,
}).then(pickedImage => {
const image = {
id: nextId.current,
path: pickedImage.path,
};
setImages(images.concat(image));
nextId.current += 1;
});
};
const takePhoto = async () => {
const result = await camera.current.takePhoto({
flash: flash,
});
if (result) {
const image = {
id: nextId.current,
path: result.path,
};
setImages(images.concat(image));
nextId.current += 1;
}
};
추가된 사진 제거
- 사진을 촬영하거나 앨범에서 사진을 가져오면 사진이 아래 두 네모칸에 저장되는데 ❌ 버튼을 눌러서 삭제가 가능
- 사진 찍기 이전에 사용자에게 빈 네모칸을 보여주기 위해서 아래와 같이 <View> 컴포넌트를 두 개로 나눠야 했음
- 빈 네모칸이 아니라면 map을 활용하여 코드를 줄일 수 있지만, 빈 네모칸을 보이기 위해서 똑같은 코드를 두 번 입력했기 때문에 아쉬운 부분
const removePhoto = id => {
setImages(images.filter(image => image.id !== id));
};
<View style={styles.contentImageBox}>
{images[0] && (
<>
<Image style={styles.imageShot} source={{ uri: `file://${images[0].path}` }} />
<Pressable style={styles.closeBtnWrapper} onPress={() => removePhoto(images[0].id)}>
<ImageWrapper style={styles.closeBtn} source={require('@/사진경로/close.png')} />
</Pressable>
</>
)}
</View>
<View style={styles.contentImageBox}>
{images[1] && (
<>
<Image style={styles.imageShot} source={{ uri: `file://${images[1].path}` }} />
<Pressable style={styles.closeBtnWrapper} onPress={() => removePhoto(images[1].id)}>
<ImageWrapper style={styles.closeBtn} source={require('@/사진경로/close.png')} />
</Pressable>
</>
)}
</View>
프로젝트 후기
웹만 다루다가 앱 개발을 기업 협업에서 처음 다루게 되었는데 Xcode, Android Studio, 시뮬레이터 등 모바일 환경에 적응하는데 상당한 시간을 빼앗겼다. 간단한 이론부터 공부해야 했기에 본격적으로 개발에 뛰어든 시간은 그렇게 길지 못했던 것 같다. 특히 협업을 진행하면서 웹 개발에 집중해야 될 시기에 앱 개발을 하고 있다는 점이 나에게 불리하게 작용하는 것은 아닐까 수 없이 고민했다. 하지만 프론트엔드로서 앱 개발 경험도 중요할 것이라는 위코드 매니저님의 말을 듣고, 협업 기간 동안 열심히 해보자고 마음을 잡게 되었다. 덕분에 스타트업에서 UI 및 기능을 직접 고민하면서 처음부터 끝까지 만들어본 경험을 할 수 있었다.
그리고 협업 중 개발자로서 첫 기술면접을 진행했다. 면접을 해보는 게 좋지 않겠냐는 말에 대뜸 신청을 하게 되었고, 준비가 되지 않은 상태에서 다음날 바로 면접을 진행하게 되었다. 진행하는 동안 제대로 된 대답을 하나도 할 수 없었기에 엄청 민망했지만, 내가 어떠한 점을 놓치고 있었는지, 부족했는지 알 수 있는 계기가 되었다. 좋은 소식을 듣지는 못했지만 면접 질문 하나하나를 다시 떠올리면서 정리하는 시간을 가졌다.