4주 프로젝트 다이어리 - sprint3

김수지·2020년 2월 12일
0

Dev Log

목록 보기
8/12

4 Weeks Project Diary

#Sprint3 - Day 6.
Javascript를 배우고 있습니다.
현재는 길고양이들을 돌보는 모바일 앱을 제작하는 4주 프로젝트 중입니다.


1. Sprint3

1. 목표(70%) vs 달성(55%)

  • 주말 포함 6일 간의 3번째 스프린트가 마무리 되었다. 3번째 스프린트를 마치면 프로젝트 전체를 100으로 보았을 때 70 수준의 결과물을 얻을 것으로 예상했다. 그리고 스프린트를 마무리 짓는 오늘 보니 반을 조금 넘는 55% 정도까지 구현한 것 같다.
  • 지난 스프린트에 이어서 역시나 프런트 진도가 빠듯했다. 구현해야 하는 컴포넌트도 많았고, 거기다가 예상치 못했던 google map 렌더 이슈 때문에 프런트 동료와 함께(동료가 나보다 더) 고생을 했고 사실은 아직도 디버깅을 못해서 마음이 좀 무겁다.
  • 3번째 스프린트 리뷰 시작

2. To-do와 회고

1. to-do: 컴포넌트 구현

1. 길고양이 화면 구현

  • 지난 스프린트에서 구현해야 했던 인증과 길고양이 등록 화면이었다면 이번 스프린트에서는 그렇게 등록된 길고양이에 대한 메인 화면 구현이 과제였다.
  • 길고양이 화면은 처음 기획과 구조를 할 때 자잘한 기능들을 모아놓은 페이지였기 때문에 컴포넌트 구조와 목업 페이지를 의식적으로 반복 참조하며 하나씩 컴포넌트를 구현해나갔다.
  • 길고양이 화면 기획내용과 구조

2. 탭 구현: 고양이 정보

  • 길고양이 등록 페이지에서 처음 길고양이를 등록한 유저의 정보를 기준하여 고양이 정보가 렌더되는 컴포넌트를 구현했다.
  • 단순 등록된 정보를 렌더하는 것 뿐만 아니라 해당 고양이 화면을 보고 있는 유저들이 스스로 post할 수 있는 '오늘의 고양이 건강 상태', '고양이 태그' 등의 기능도 마련했다.
  • '오늘의 고양이 건강 상태'의 경우에는 매일 refresh하여 길고양이 돌보미 사용자들이 이 화면에 도착했을 때 하루에 한 번만 제공되는 고양이 상태 선택을 먼저 선점(?)할 수 있게 해서 재미를 느낄 수 있도록 기획했다.
  • 대신 db에서 데일리 고양이 건강 상태는 굳이 누적할 만한 데이터가 아닌 것 같아서 클라이언트에서 '오늘의 고양이 상태'를 매일 override 할 수 있도록 아래와 같이 코드를 짰다.
    <Text style={styles.width100}>오늘 {cat.nickname}의 건강 상태</Text>
    <Text>{cat.today}</Text>
    // catToday가 존재한다면 등록일자와 오늘일자가 같을 떄만 보여주고
    {cat.today && makeDateTime(cat.todayTime) === makeDateTime(new Date()) ?
     (<Text>{cat.today}</Text>)
      :
      //등록일자보다 오늘이 더 최근이면 선택할 수 있도록 구현
     (<Form style={{width: '100%',flexDirection: 'row',}} />
     ... 생략```

3. 탭 구현: 포스트, 댓글 인풋

  • 그런 다음 구현한 탭은 고양이에 대한 글과 사진 그리고 댓글 소통이 가능한 포스트란이었다.
  • 이 부분도 모두 구현을 마치는 것이 목표여서 포스트 부분은 동료가, input 창 부분은 내가 구현하기로 했는데 아직 머지를 못한 상태이다.
  • 아이콘을 눌러서 이미지를 등록하고, 등록된 사진이 미리보기로 보여지고, 사진을 삭제하는 부분을 처음 구현해보면서 그 로직을 익힌 것 같다.
    <View style={styles.container}>
      <View style={styles.inputView}>
      // 메시지 입력 인풋 창
        <Form style={styles.inputForm}>
          <Textarea
          rowSpan={inputContent.length > 27 ? 4 : 2}
          placeholder="글을 입력해주세요."
          value={inputContent}
          onChangeText={text => updateInput('info', 'inputContent', text)} />
        </Form>
        <View>
          <View style={styles.inputBottomView}>
            <View style={styles.imageView}>
            // 등록된 이미지 uri가 있다면 이미지 렌더
            {uri ? (
              <View>
                <TouchableHighlight
                  style={styles.removeBtn}
                  underlayColor="#ffece0"
                  onPress={removePhoto}>
                  // 이미지를 지우는 x 버튼 삽입
                  <Text>style={styles.removeBtnTxt}>X</Text>
                </TouchableHighlight>
                <Image> style={styles.image} source={{ uri }} />
              </View>
               ) : (
              // 등록된 이미지가 없다면 이미지 첨부 기능 노출
              <TouchableOpacity
                style={styles.addImageBtn}
                onPress={async () => {
                  await getPermissionAsync();
                  pickImage('info');
                  }}>
                   <SimpleLineIcons style={styles.imageIcon} name="picture" />
                   <Text style={styles.addImageTxt}>이미지 첨부(1)</Text>
               </TouchableOpacity>
               )}
             </View>
             <View>
               // 게시글과 사진 등록 버튼
               <TouchableOpacity
                 style={styles.submitBtn}
                 onPress={() => {
                   const validation = validateAddInput('inputContent');
                   console.log(validation);
                   if (validation) {
                     addPost();
                   }
                 }}
               >
                 <Text style={styles.submitBtnTxt}>등록</Text>
               </TouchableOpacity>
             </View>
           </View>
           <View style={styles.paddingTop5} />
         </View>
       </View>
     </View>

4. 탭 구현: 고양이 사진 앨범

  • 고양이 사진 앨범은 앞선 탭에서 등록된 사진만을 모아서 인스타그램처럼 보여주는 view를 예정했고, 구현하는 기능 중 가장 난이도가 낮았다.
  • 다만 사진을 확대해서 보거나 다운로드 받을 수 있도록 고양이 사진 클릭 시 모달 창을 띄워줄 수 있도록 react navigation을 이용해 고양이 정보 페이지(4개 탭을 가지고 있음) > 고양이 사진 클릭 > 고양이 사진 모달 창> 고양이 사진 컴포넌트 렌더의 방식으로 구현했다.

5. 탭 구현: 고양이 팔로워

  • 마지막으로 고양이들을 팔로우하는 팔로워 리스트를 구현했고, 팔로워 정보를 store에서 배열로 받아와서 map 메소드를 사용하여 고양이 팔로워라는 컴포넌트를 연달아 렌더시키는 방식으로 구현했다.

6. 완료된 화면

2. to-do: 외부 모듈 사용

1. reverse geocoding

  • 앞서 말한 컴포넌트 구현 이외에도 외부 모듈을 사용하여 구현해야 하는 부분이 있었다. 메인 화면에서 고양이 위치들을 지도 상의 마커들로 표현하고 있어서 고양이 상세 정보 페이지로 들어오게 되는 경우 위,경도로 표시되어 있는 마커를 '00도 00시 00구' 등의 주소 정보로 변환시켜주는 작업이 필요했다.
  • 우리는 google map을 사용하고 있기 때문에 google map에서 제공하는 geocoding/reverse geocoding이라는 기능을 검색해서 찾을 수 있었는데 geocoding은 주소를 좌표로, reverse geocoding은 좌표를 주소로 변환시켜주는 방법이었다.
  • 구현 자체는 어렵지 않았으나 구글에서는 이 서비스를 호출 기준으로 유료 제공하고 있었기 때문에 대안으로 카카오(월별 일정 건수는 무료로 제공 중)에서 동일한 기능을 찾았다.
  • 전체 플로우를 보자면 길고양이 등록 시에는 구글 맵을 통해 좌표를 db에 저장하였다가 고양이 상세 정보 페이지에 렌더하면서 카카오 reverse geocoding을 통해서 일부 주소로 구현해주는 방법을 택했다. 비용 효율을 위해서 사용자가 길고양이 등록 시 좌표를 찍을 때마다 api를 호출하지 않고 등록 직전의 좌표만을 활용하도록 코드를 구현했다.
getAddress = async () => {
    const { latitude, longitude } = this.addCatBio.location;
   
    axios
      .get(    `https://dapi.kakao.com/v2/local/geo/coord2address.json?x=${longitude}&y=${latitude}&input_coord=WGS84`,
        {
          headers: {
            Authorization: `KakaoAK ${KAKAO_MAPS_API_KEY}`,
          },
        },
      )
      .then(res => {
        const {
          region_1depth_name,
          region_2depth_name,
          region_3depth_name,
        } = res.data.documents[0].address;
        console.log(region_1depth_name, region_2depth_name, region_3depth_name);
        this.addCatBio.address = `${region_1depth_name} ${region_2depth_name} ${region_3depth_name}`;
        return this.addCatBio.address;
      })
      .catch(err => {
        console.dir(err);
        Alert.alert('좌표가 정확하지 않습니다. 다시 지도에서 선택해주세요!');
      });
    return true;
  };

2. image picker 적용

  • 이미지 등록과 관련해서 적용했던 모델은 expo에서 제공하는 image picker였다.
  • 이 서비스에서 이미지 등록이 필요한 곳은 총 3곳으로, 최초 길고양이 사진 등록, 길고양이 포스트에서 사진 등록, 마지막으로 유저 사진 등록이었다.
  • 모두 1장씩 등록할 수 있으며 1) 사진 등록을 위해서 핸드폰 카메라에 접근 가능하도록 허용을 받는 프로세스를 구현할 것, 2)base64로 인코딩하여 문자열화 시키기, 3) 사진 비율을 4:4로 정사각형으로 구현할 것, 4) compress를 진행할 것이 전제 조건이었고 image picker에서 option으로 모두 구현이 가능했다.
    // 핸드폰 카메라에 접근할 수 있도록 허용 받기
    getPermissionAsync = async () => {
      if (Constants.platform.ios) {
        const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
        if (status !== 'granted') {
          Alert.alert('사진을 올리기 위해 접근 권한이 필요합니다.');
        }
      }
    };
    // 사진첩 or 갤러리에서 정사각형으로 이미지를 고른 후 base64로 인코딩하는 함수
    pickImage = async type => {
      const result = await ImagePicker.launchImageLibraryAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.All,
        allowsEditing: true,
        aspect: [4, 4],
        quality: 1,
        base64: true,
      });
      if (!result.cancelled) {
        this[type].uri = result.uri;
        this[type].photoPath = result.base64;
      }
    };```
    

3. 회고

1. 어려움을 느꼈던 부분들

  • 이번 스프린트에서 어려웠던 부분은 사실 꾸준히 컴포넌트 위에 컴포넌트를 얹는 작업이었다. 하나씩 화면으로 구현이 되어서 성취감도 컸지만 반복적인 작업이어서 지치는 면도 있었던 것 같다.
  • 이번 스프린트에서 주로 프런트 동료의 task였던 지도 렌더 부분에서 현재 위치 기준으로 좌표를 잡지 못하는 버그 부분 때문에 3일 가까이 고생을 했다. 함께 코드를 읽고 디버깅을 도와야 하는데 구글맵에서 제공하는 함수 부분과 store를 활용하는 부분을 이해한 후에 디버깅에 의견을 더하는 것이 쉽지 않았다.

2. 극복을 위한 노력들

  • 반복적인 컴포넌트 구현은 동료들과의 데일리 코드 리뷰를 통해서 어떤 로직으로 작성하고자 했는지 발표하면서 지친 감정을 떨쳐냈다. 내가 먼저 구현해보고 함께 공유한다는 생각으로 작업을 하니 조금 더 동기가 생겼다.
  • 지도 렌더 부분은 함께 스택오버플로우를 찾거나 부트캠프 엔지니어분에게 우리가 짰던 로직을 설명하면서 디버깅을 하려고 노력했다. 이를 통해서 비동기적 정보 전달에 문제가 있다는 점을 깨달았다. (아직 문제를 풀지는 못했지만) 디버깅을 위해서 계속해서 엔지니어분들과 논의를 통해서 풀어나가려고 한다.

3. 배움

  • 리액트 네이티브에서의 레이아웃 구현이나 일반적으로 서비스에서 자주 구현하는 리스트, 이미지 첨부/취소, 태그 등록/렌더 등을 실제로 작업하면서 코딩해볼 수 있었다.
  • 많지는 않지만 카카오나 구글 등에서 제공하는 외부 api를 활용하여 정보를 받아오는 것도 익힐 수 있었다.

4. 다음 스프린트 계획

  • 주말 포함 6일로 구성되어 있는 다음 스프린트에서는 유저의 마이페이지 부분 컴포넌트를 작성하고, 서버에서 제공하는 Oauth 방식의 토큰을 인증 과정에 적용하는 작업을 진행할 것이다. 서버와의 연동을 통해서 api를 활용한 핸들러 함수의 테스트와 디버깅을 진행하려고 한다.
profile
선한 변화와 사회적 가치를 만들고 싶은 체인지 메이커+개발자입니다.

0개의 댓글