Loading..

Prisma 2에서 Subscription 구현하기

2021. 7. 16. 14:39

반응형

Prisma 2에서 Subscription 구현하기

요약

Prisma 2에서는 Subscription을 지원하지 않는다. (Issue 링크)

이슈 게시글을 보면 Subscription을 지원하기 위해 작업하고 있다고 적혀 있지만, Prisma 팀에서 공개한 로드맵에도 Subscription은 존재하지 않는다.

그러면 어떻게 하지? 직접 만들어야지 뭐 😮‍💨

작업 환경

필자가 작업하는 환경은 아래와 같다.

  • 언어는 TypeScript 사용
  • 제목과 같이 Prisma 2 ORM + nexus
  • 웹 서버로 fastify + mercurius 사용
  • AWS Lambda 환경에 구축되어 있음

아이디어를 얻어보자 🧐

다행히, Prisma 2에서 Subscription을 지원하지 않아 곤란한 사람은 우리만 있는게 아니다.

위에서 링크해둔 이슈 게시글만 봐도 많은 사람들이 아이디어를 공유하고 있다.

Mercurius에서는 Subscriptions을 지원하고 있어, 해당 방법을 통해 구현해보고자 한다.

Mercurius에서 제공하는 Subscriptions 관련 Document는 아래 링크에서 확인 가능하다.

https://mercurius.dev/#/docs/subscriptions

구현

우선 WebSocket 사용을 위해 fastify-websocket을 설치한다.

yarn add fastify-websocket

그리고, 기존 mercurius 설정이 있는 곳에 아래 코드를 추가한다.

코드의 내용은 fastify-websocket을 import하고, fastify 앱에 등록하는 코드이다.

// ..import
import fastifyWebsocket from 'fastify-websocket';

// ..codes
app.register(fastifyWebsocket, {
  options: {
    maxPayload: 1048576,
  },
});

이제, 기존 mercurius 설정 부분 수정 및 websocket endpoint를 설정하겠다.

// 기존 mercurius 설정에 subscription: true 추가
app.register(mercurius, {
  schema,
  context: (req) => ({ prisma, req }),
  graphiql: false,
  subscription: true,
});

// 엔드포인트 추가
app.get('/', { websocket: true }, (connection) => {
  connection.socket.on('message', () => {
    connection.socket.send('ws connected');
  });
});

그 다음, nexus를 통해 Subscription type을 지정하겠다.

필자는 아래와 같이 지정했다.

const Subscription = objectType({
  name: 'Subscription',
  definition(t) {
    t.nonNull.field('notificationAdded', {
      type: 'Notification',
      args: {
        userId: nonNull(intArg()),
      },
      resolve: async (_, { userId }, { pubsub }: Context) => await pubsub.subscribe(`NOTIFICATION_ADDED_${userId}`),
    });
  },
});

그리고 Subscription을 Schema에 추가한다.

const schemaWithoutPermissions = makeSchema({
  types: [
    Query,
    Mutation,
    Subscription,
        // ...
  ],
  // ...
});

이제, Mutation에 subscription을 publish 하는 코드를 추가하면 된다.

await context.pubsub.publish({
  topic: `NOTIFICATION_ADDED_${userId}`,
  payload: {
    notification,
  },
});

이제 설정이 모두 끝났다.

해당 코드를 deploy하고 클라이언트에서 테스트 해보면 정상 작동을 확인할 수 있다.

반응형

오랜만에 블로그 업데이트

2021. 3. 25. 14:53

반응형

오랜만에 블로그 업데이트

대충 하루 만에 뚝딱 만들어두고 유지 보수가 전무했는데, 수정할게 없어서 전무했던 건 아니고 시간이 없어서 수정이 없었음

뭘 하나 누를 때마다 문제가 하나씩 있어서.. 점점 고쳐 나가거나 싹 뜯어 바꿔야 함

모바일도 만들어야 하고..

그래서 업데이트 한 거랑 이제 업데이트 해야 하는 것들.

👍 업데이트 내용

  • 우측 하단 최상단으로 가기 버튼 동작 안 하는 오류 수정
  • Profile 페이지 업데이트
    • 사실 이건 짬짬이 업데이트는 하고 있었음 ㅎㅎ
  • 검색 한 글자씩 밀리는 거
    • 원래 "알고리즘"으로 검색하면 "알고리" 로 검색한 결과가 노출되고 있었음

👎 아는데 안 한 거

  • 모바일 지원
    • 귀찮고.. 디자인도 해야 하고..
    • 근데 솔직히 티스토리 기본 모바일 스킨 괜찮지 않나
  • url에 #이 붙어 있는 채로 접속 시 뒤틀린 황천의 페이지로 이동함
    • #이 붙은 건 원본 페이지로 이동 시키고.. 없는건 404 띄우고..
  • 두 글자 검색 이상하게 되는거
    • 검색 로직을 싹 바꿔야지.. 과거의 유물이다..
  • utterances 떼고 티스토리 기본 댓글로 변경
    • 이거 변경하는 이유는 utterances 다니까 댓글이 너무 없어서 재미없음 ㅡㅡ
    • PC는 utterances인데 모바일은 티스토리 기본 댓글인 것도 수정 사유 중 하나
  • 이 게시글 마크다운으로 옮기면서 알았는데 게시글 내에 ul 태그도 list-style이 싹 다 지워지네 ㅡㅡ 이것도 고칠거임 언젠간

하여튼 이제 블로그에 신경 좀 써보려고 한다.

근데 내가 없는 동안 PostgreSQL JDBC 연동 글이 엄청 인기를 끌던데 언어 별로 DB 연동 만들어두면 거의 연금이 되지 않을까?

반응형

퇴사 후기

2021. 3. 23. 18:26

반응형

퇴사 후기

얼마 전 2년간 다니던 회사를 나왔다.

오늘은 첫 직장을 퇴사하고 난 뒤의 생각들을 두서 없이 써보려고 한다.

사실 가장 큰 감정은 신나는 거긴 한데 그렇게 적으면 없어 보이니까 패스

🤔 무슨 회사에 다녔는데요?

프로필 봐라

필자는 2년간 디자인 에이전시에 근무했다.

1년은 백엔드 개발자로 근무하고, 1년은 개발팀 팀장으로 근무했다.

흔히 에이전시를 개발자의 무덤, 개발자의 지옥이라고 얘기하는데 내가 느낀 에이전시는 딱 그 중간의 느낌이었다.

백엔드 개발자로 근무할 때는 편하고 워라밸도 좋았지만, 개발팀 팀장으로 근무할 때는 퇴근과 주말이 없는 삶을 살았다.

1년간 누렸던 워라밸을 1년간 반납한 느낌?

🧑‍💻 어떤 일을 하셨었나요?

이것도 1년 주기로 달라짐

처음 1년은 백엔드 유지보수 / 신규 프로젝트 백엔드 개발 업무를 했고, 팀장 1년은 PM, 팀원 관리, 인프라 구축, 프론트엔드 개발, 백엔드 개발, 어플리케이션 개발, 유지보수, 외주 프로젝트 관리를 했다.

거의 슈퍼맨으로 일한 느낌?

퇴사하고 나서도 전 직장에서 인수인계 관련 요청, 클라이언트에게 문의 전화가 계속 왔다

🙋‍♂️ 왜 퇴사하셨어요?

가장 큰 이유는 일이 너무 빡세서.

물론 다른 이유도 겹쳤지만, 일이 너무 빡세고 내 인생이 없어서 그만둔게 가장 컸다.

항상 야근에, 주말 출근이 계속되다보니 여가시간 자체가 없었고, 내 생활이 없었다.

그렇게 살다보면 당장 신체적으로 힘든 것보다 정신적으로 많이 힘들어지는데, 퇴사 결정을 할 때는 내 정신력이 거의 바닥까지 가있었다.

🤭 퇴사하고 나서는 뭐하고 계세요?

유튜브 보기

하고 있는건 상당히 많다.

로스트아크도 다시 시작했고, 그동안 못 만났던 친구들도 만나고 그러면서 미래를 위해서 다른 준비를 하고 있기도 하다.

사실상 일하는 백수로 살고 있는 상태

근데 가장 만족감이 느껴지는건, 어떤 일을 해도 재밌고 숨 돌릴 수 있는 여가 시간이 있다는 점이다.

🤷‍♂️ 그래서 무슨 말이 하고 싶은건데요?

에이전시 다니지 마라

하고 싶은 말은 없고, 그냥 내가 어떻게 살고 있는지 얘기하고 싶었다.

그래도 위 시간들을 보내면서 느낀 점이 있다면, 내가 진짜 감당할 수 없을 정도의 일을 하고 있고 너무 힘들다면 그냥 그만둬라.

그걸 몇 년동안 붙잡고 있어봤자, 과도한 일로 배우는 건 크게 없고 오히려 능력을 저하시킬 뿐이니까.

반응형

AirPods Pro 리뷰

2019. 11. 24. 16:40

반응형

AirPods Pro 리뷰


AirPods Pro 국내 출시 당시 바로 구입해서 지금까지 사용해본 경험을 통한 리뷰입니다. 제가 iPhone을 사용하고 있기 때문에, 리뷰에서 얘기하는 기준은 iPhone + AirPods Pro 기준의 사용 경험입니다.

사용성

사실 요즘 코드리스 이어폰들도 다들 사용성이 편리해져서 그렇게 큰 의미는 없긴 하지만, 확실히 아이폰에서 사용할 때는 에어팟이 가장 편한 것은 사실입니다. 사용성 부분에서는 불편한 부분도 없고 연결 속도도 빨라서 편했습니다. 특히 PC 등 다른 기기에 연결해서 사용하다가 전화가 왔을때 에어팟 프로로 전환해서 사용할 때가 많았는데, 전환 속도가 빨라서 만족스러웠습니다. 이 부분은 현세대 코드리스 이어폰들이 모두 그렇듯 문제 삼을 부분은 없습니다.

음질

애플의 이어팟, 에어팟 계열의 음질은 항상 좋게 말하자면 밸런스가 잡혀 있고, 나쁘게 말하자면 특색이 없는 음질입니다. 하지만 저는 밸런스 잡힌 음질을 좋아하는 편이라서 만족스럽게 사용했습니다. 특히 오픈형인 전작들과 비교하면 형태로 인해 밖으로 빠져나가는 소리가 없어서 더 만족스러웠습니다. 다만 저는 음향에는 크게 조예가 깊지 않아, 제 의견은 그냥 참고만 하시는 걸 추천드립니다.

노이즈 캔슬링

저는 AirPods Pro 전에 노이즈 캔슬링 기능이 있는 헤드폰이나 이어폰을 사용해본 경험이 없습니다. 저는 노이즈 캔슬링을 에어팟 프로를 통해서 처음 접했는데, 접하기 전 다른 사람들이 말하는 것처럼 우주에 혼자 떠 있는 경험이라던가, 그런 경험을 하지는 못했습니다. 하지만 일상생활에서는 성능의 체감이 확 되는 편입니다. 일단 데스크탑을 사용할 때 에어팟 프로를 사용하며 노이즈 캔슬링을 켜면 쿨러 소리가 바로 사라지고, 지하철에서도 주변 소음이 확 사라져서 음악 뿐만 아니라 조금 사운드가 작은 영상을 볼때에도 훨씬 몰입 할 수 있었습니다. 에어팟 2세대를 사용할 때는 지하철에서 최대 볼륨으로 사용해도 영상의 소리가 주변 소음에 묻히는 경우가 없었는데, 이제는 소리를 중간 보다 낮게 설정해도 영상에 몰입 할 수 있어서 만족스러웠습니다. 그렇다고 안내방송이 안들리는 건 아니라서 내려야 할 역을 놓치거나 하는 경우도 없었구요. 에어팟 프로의 노이즈 캔슬링은 듣기 싫은 반복적인 소음들은 확실히 제거해주고, 말 소리나 안내 방송 같은 소리들은 작게 들어와서 만족스러웠습니다. 또한, 주변음 듣기 모드도 만족스러웠습니다. 노이즈 캔슬링에서 주변음 듣기 모드로 딱 전환하면 물 속에 있다가 밖에 나갈 때 귀가 뚫리는 그 느낌이 들었습니다. 주변음 듣기 모드를 켠다고 음악이 안들리는 건 또 아니라서, 지하철과 실내에서를 제외하면 대부분 주변음 듣기 모드를 켜고 사용하고 있습니다.

가격

비쌉니다. 많이 비싸요. 다만 에어팟의 경우는 사용하면서도 가끔 가격의 의구심이 드는 가격이었다면, 에어팟 프로는 사용하고나면 적당한 가격이라는 세뇌를 당하는 느낌입니다. 노이즈 캔슬링 기능이 있는 다른 이어폰들과도 비슷한 가격대인 것도 한 몫 하는 것 같습니다. 정리하자면 사용하기 전에는 많이 비싼 가격이고, 사용하고 나면 가격이 수긍이 가는 것 같습니다.

총평

지금까지 사용했던 코드리스 이어폰 중 가장 만족스럽습니다. 약간의 과장을 보태자면 이제 노이즈 캔슬링이 없는 다른 이어폰은 사용하지 못할 것 같습니다. 저는 에어팟 2세대를 사용하다가 분실하고, 그 후로 유선 이어폰을 사용하다가 다시 에어팟 프로를 구매하면서 코드리스로의 회귀였습니다. 그렇다 보니 코드리스가 주는 무선의 편안함과, 좋은 성능의 노이즈 캔슬링이 한 번에 몰려와서 더 만족스러웠던 것 같습니다. 에어팟 1세대를 사용하시거나, 에어팟 2세대를 구매하신지 꽤 되셨다면, 에어팟 프로로 업그레이드 하시는 것도 괜찮을 것 같습니다.

반응형

BTT 카카오톡 프리셋 개발 후기

2019. 10. 22. 11:43

반응형

BTT 카카오톡 프리셋 개발 후기

macOS 유저들이 많이 사용하는 BetterTouchTool이라는 트윅이 있습니다. 저는 터치바 관련 트윅을 위해 BTT와 GoldenChaos-BTT 프리셋을 이용하고 있는데, 페이스북 메신저 등의 알림수를 표시해주는 아이콘이 마음에 들어서 카카오톡도 이렇게 볼 수 있으면 좋겠다 싶어 해당 아이콘을 개발했습니다.

갑자기 확 꽂혀서 만들었는데, 기존 아이콘들을 참고하니 생각보다 만들기 간단했습니다. 만든 김에 해당 프리셋을 공유하려고 글을 작성했습니다.
https://gist.github.com/kentakang/7852bf52201ff5c40e53828b7008f9ac
프리셋은 위 링크에서 다운로드 하실 수 있습니다.

반응형

IntersectionObserver로 구현하는 React 무한 스크롤

2019. 8. 17. 17:22

반응형

IntersectionObserver로 구현하는 React 무한 스크롤

오늘은 React에서 IntersectionObserver를 통해 무한 스크롤을 구현해보려 합니다. 요즘 SPA에서는 보통 페이징을 무한 스크롤을 통해 구현하는 경우가 많은데, 무한 스크롤을 구현하는 방법으로는 여러가지가 있습니다. 이번에는 자바스크립트의 Intersection Observer API를 사용해서 구현해보겠습니다.

Intersection Observer가 뭔데요?

IntersectionObserver API는 element가 viewport, 다른 엘리먼트와의 관계에서 보이는지 안보이는지를 알 수 있도록 하는 API입니다. 많이 사용되는 경우는 지금 제가 구현하려고 하는 무한 스크롤이나, 이미지의 lazyload 등이 있겠습니다. 현재 IE를 제외한 대부분 브라우저의 최신 버전에서는 지원하고 있고, 디테일한 버전은 caniuse 에서 확인 하실 수 있습니다.

Intersection Observer를 어떻게 활용하나요?

IntersectionObserver 객체의 경우 callback, options를 매개변수로 받습니다. options 객체의 경우 root, rootMargin, threshold 값을 받습니다.

callback

callback 함수는 관측 대상이 threshold 만큼 보일 때, 호출되는 함수입니다. 관측 대상으로 지정한 DOM Element 객체가 entires, observer 자기 자신인 observer가 매개 변수로 전달됩니다.

root

root의 값은 element, null 중 하나입니다. 관측 대상을 감싸는 element가 들어가거나, null로 지정할 경우 viewport가 들어갑니다. 문서 내에 따로 스크롤이 가능한 요소가 있고, 관측 대상이 해당 요소 안에 있다면 root에는 스크롤 가능한 요소가 들어가게 됩니다.

rootMargin

rootMargin의 경우 위에서 지정한 root 요소를 감싸는 margin 값이 들어갑니다. css margin과 같이 px, % 등의 단위로 작성할 수 있습니다.

threshold

관측 대상이 root와 몇 % 교차했을때, 지정해준 callback 함수를 실행할지 결정하는 값입니다. threshold의 값은 float 값이나, float의 배열이 들어갈 수 있습니다. 만약 마지막 요소가 10% 정도 보일때, 다음 페이지 요소를 로딩하는 callback을 실행한다면, threshold의 값은 0.1이 됩니다.

IntersectionObserver의 구성 요소를 알아봤으니, 실제로 구현해보도록 하겠습니다.

무한 스크롤 구현

우선 제가 구현하고 있는 페이지는 아래 코드와 같이 작성되어 있습니다.

import React, { useState } from "react";
import { useQuery } from "react-apollo-hooks";
import {
  Page,
  Header,
  Content,
  Search,
  GridList,
  Card
} from "../components/Page";
import { getThumbURL } from "../utils";
import GET_VIDEOS from "../Queries/Video";

const Main = () => {
  const [page, setPage] = useState(1);

  const { data } = useQuery(GET_VIDEOS, {
    variables: {
      page
    }
  });

  return (
    <Page>
      <Header>
        <Search placeholder=" 검색어를 입력해주세요" />
      </Header>
      <Content>
        <GridList>
          {data !== undefined &&
            data.videos.map(video => (
              <Card
                key={video._id}
                thumb={getThumbURL(video.filePath)}
                name={video.name}
              />
            ))}
        </GridList>
      </Content>
    </Page>
  );
};

export default Main;

마지막 엘리먼트의 ref를 추가하고, 해당 ref를 통해 IntersectionObserver를 통한 무한 스크롤을 구현해보도록 하겠습니다.

import React, { useState, useRef, useEffect } from "react";
import { useQuery } from "react-apollo-hooks";
import {
  Page,
  Header,
  Content,
  Search,
  GridList,
  Card
} from "../components/Page";
import { getThumbURL } from "../utils";
import GET_VIDEOS from "../Queries/Video";

const Main = () => {
  const [page, setPage] = useState(1);
  const [videos, setVideos] = useState([]);
  const lastCardRef = useRef(null);
  const intersectionObserver = new IntersectionObserver((entries, observer) => {
    const lastCard = entries[0];

    if (lastCard.intersectionRatio > 0) {
      observer.unobserve(lastCard.target);
      lastCardRef.current = null;

      setTimeout(() => {
        setPage(page + 1);
      }, 100);
    }
  });
  const query = useQuery(GET_VIDEOS, {
    variables: {
      page
    }
  });

  useEffect(() => {
    if (lastCardRef.current) {
      intersectionObserver.observe(lastCardRef.current);
    }
  });

  useEffect(() => {
    if (!query.loading) {
      setVideos(videoList => [...videoList, ...query.data.videos]);
    }
  }, [query]);

  return (
    <Page>
      <Header>
        <Search placeholder=" 검색어를 입력해주세요" />
      </Header>
      <Content>
        <GridList>
          {videos.length &&
            videos.map((video, idx) =>
              idx !== videos.length - 1 ? (
                <Card
                  key={video._id}
                  thumb={getThumbURL(video.filePath)}
                  name={video.name}
                />
              ) : (
                <Card
                  key={video._id}
                  thumb={getThumbURL(video.filePath)}
                  name={video.name}
                  ref={lastCardRef}
                />
              )
            )}
        </GridList>
      </Content>
    </Page>
  );
};

export default Main;

저는 이런 식으로 작성했습니다. 우선 lastCardRef에 마지막 카드의 ref를 넣었습니다. 그리고 페이지가 렌더링 될 때마다 useEffect 훅을 이용해, lastCardRef.current의 값을 observe 해주도록 했습니다. 그 다음 intersectionObserver의 콜백 함수에서는 intersectionRatio를 이용해 해당 엘리먼트가 보이는지 체크하고, 보일 경우 lastCardRef의 current를 null로 변경하고, 기존 관측 대상을 unobserve 한뒤 페이지를 1 더해주는 방식으로 처리했습니다. 이렇게 무한 스크롤 기능을 구현해보니, 기존 viewport의 높이를 구하고 스크롤 이벤트를 줘서 무한 스크롤을 구현하는 방식보다 편리했습니다.

반응형

Serverless 배포 시 Webpack과 Babel 사용하기

2019. 7. 24. 16:57

반응형

Serverless 배포 시 Webpack과 Babel 사용하기

최근 프로젝트에 Serverless Framework를 사용하고 있습니다. GraphQL 백엔드를 AWS Lambda로 배포하기 위해 사용하는 데, 저는 Node.js 프로젝트를 진행할 때 Webpack, Babel을 통해 ES2016을 사용해서 작업하는 편입니다. 근데 보통 Serverless를 통해 배포하는 예제들의 경우 Webpack, Babel을 이용하는 방법이 나와있지 않아서 serverless-webpack 이라는 플러그인을 통해 Serverless에서 Webpack을 사용하는 방법을 정리해보려고 합니다.

Requirements

  • 패키지 매니저로 Yarn을 사용하고 있습니다.
  • 기본적인 Serverless 환경이 구성되어 있음을 전제로합니다.

설정

우선 플러그인 사용을 위해 serverless-webpack을 devDependencies로 설치하겠습니다.

yarn add -D serverless-webpack

그 다음 serverless.yml에 플러그인을 추가해주세요.

plugins:
  - serverless-webpack

그 다음 Webpack을 설치하고 설정하겠습니다. 저는 Webpack을 통해서 ES6을 사용할거기 때문에, 해당 설정에 맞게 패키지를 설치하겠습니다. 먼저 babel을 설치하겠습니다.

yarn add -D @babel/core @babel/cli @babel/preset-env

그 다음 프로젝트 디렉토리에 .babelrc를 생성해서 babel 설정을 하겠습니다.

{
  "presets": ["@babel/preset-env"]
}

그 다음 webpack을 설치하겠습니다.

yarn add -D webpack webpack-node-externals

webpack에서 babel을 사용할 수 있게 babel-loader도 설치하겠습니다.

yarn add -D babel-loader

그 다음 webpack.config.js를 작성하겠습니다.

const slsw = require("serverless-webpack");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  externals: [nodeExternals()],
  mode: slsw.lib.webpack.isLocal ? "development" : "production",
  module: {
    rules: [
      {
        loader: "babel-loader",
        include: __dirname,
        exclude: /node_modules/
      }
    ]
  }
};

그 다음 serverless.yml에 webpack 관련 설정을 추가하겠습니다.

custom:
  webpack:
    webpackConfig: ./webpack.config.js
    includeModules: true

그 다음 deploy 해보시면 제대로 작동하는 것을 확인하실 수 있습니다.

반응형

React Native 앱에 Splash 스크린 추가

2019. 7. 2. 20:47

반응형

React Native 앱에 Splash 스크린 추가

개요

많은 어플리케이션에서는 앱이 실행될 때 Splash 스크린을 띄워, 미리 데이터를 가져오거나 백그라운드 작업을 처리한 뒤 앱을 실행하는 경우가 많습니다. 오늘은 react-native-splash-screen 라이브러리를 통해 Splash 스크린을 구현하겠습니다.

개발 환경

  • React 버전 16.8.3, React Native 버전 0.59.9 버전을 사용하고 있습니다.
  • 패키지 매니저로 Yarn을 사용하고 있습니다.

라이브러리 설치

우선 react-native-splash-screen을 설치하겠습니다.

yarn add react-native-splash-screen
react-native link react-native-splash-screen

Splash 이미지 만들기

Splash 스크린을 추가하기 전 Splash 스크린에 보여줄 이미지가 있어야겠죠? 우선 generator-rn-toolbox를 설치하겠습니다.

npm install -g yo generator-rn-toolbox

Yarn의 경우 yeoman을 설치하는데 일부 오류가 있어 npm을 사용해주세요. 그 다음 이미지를 만들기 위한 imagemagick을 설치해야합니다. https://imagemagick.org/script/download.php 에서 설치해주세요. 그 다음 스플래시 이미지를 만들겠습니다. 우선 스플래시 이미지로 사용할 이미지는 2208x2208 사이즈의 psd 파일로 준비해주세요. 그 다음 generator-rn-toolbox를 통해 안드로이드 스플래시 스크린을 만들겠습니다.

yo rn-toolbox:assets --splash [스플래시 파일 경로] --android

이제 스플래시 스크린 설정을 하겠습니다.

라이브러리 사용

제가 Windows 환경에서 개발 중인 관계로 Android, RN 부분 설정만 작성하도록 하겠습니다. 우선 React Native 내부의 Android 폴더를 Android Studio에서 열어주세요. Android Studio에서 작업하는 것이 더 편리합니다. 우선 MainActivity.java를 수정하겠습니다.

...
import com.facebook.react.ReactActivity;
import org.devio.rn.splashscreen.SplashScreen;
...
public class MainActivity extends ReactActivity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        SplashScreen.show(this);
        super.onCreate(savedInstanceState);
    }
}

우선 org.devio.rn.splashscreen.SplashScreen을 import 하신 다음 Android Studio, IntelliJ IDEA 기준 Ctrl + O를 누르시면 Override 할 수 있는 메소드를 확인하실 수 있습니다. 거기서 ReactActivity의 onCreate를 Override 하시고 SplashScreen.show(this); 를 추가해주세요.
그리고 res/layout/launch_screen.xml 을 생성하신 뒤 아래와 같이 적어주세요.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/launch_screen" android:scaleType="centerCrop" />
</RelativeLayout>

이제 RN에서 설정을 하겠습니다.

import React from 'react';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import Home from './pages/Home';
import Meal from './pages/Meal';
import Document from './pages/Document';
import Schedule from './pages/Schedule';
import Settings from './pages/Settings';

const Navigator = createBottomTabNavigator(
  {
    Home,
    Meal,
    Document,
    Schedule,
    Settings,
  },
  {
    defaultNavigationOptions: ({ navigation }) => ({
      tabBarIcon: ({ focused, horizontal, tintColor }) => {
        const { routeName } = navigation.state;
        const IconComponent = Ionicons;
        let iconName;

        switch (routeName) {
          case 'Home':
            iconName = `ios-home`;
            break;
          case 'Meal':
            iconName = 'ios-restaurant';
            break;
          case 'Document':
            iconName = 'ios-document';
            break;
          case 'Schedule':
            iconName = 'ios-calendar';
            break;
          case 'Settings':
            iconName = 'ios-settings';
            break;
          default:
            break;
        }

        return <IconComponent name={iconName} size={25} color={tintColor} />;
      },
    }),
    tabBarOptions: {
      activeTintColor: '#007ac1',
    },
  }
);

export default createAppContainer(Navigator);

기존 제 프로젝트의 App.js 코드입니다. React Navigation을 이용하기 때문에 createAppContainer를 export 하고 있습니다. 해당 코드를 수정하겠습니다.

import React, { useEffect } from 'react';
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import SplashScreen from 'react-native-splash-screen';
import Home from './pages/Home';
import Meal from './pages/Meal';
import Document from './pages/Document';
import Schedule from './pages/Schedule';
import Settings from './pages/Settings';

const Navigator = createBottomTabNavigator(
  {
    Home,
    Meal,
    Document,
    Schedule,
    Settings,
  },
  {
    defaultNavigationOptions: ({ navigation }) => ({
      tabBarIcon: ({ focused, horizontal, tintColor }) => {
        const { routeName } = navigation.state;
        const IconComponent = Ionicons;
        let iconName;

        switch (routeName) {
          case 'Home':
            iconName = `ios-home`;
            break;
          case 'Meal':
            iconName = 'ios-restaurant';
            break;
          case 'Document':
            iconName = 'ios-document';
            break;
          case 'Schedule':
            iconName = 'ios-calendar';
            break;
          case 'Settings':
            iconName = 'ios-settings';
            break;
          default:
            break;
        }

        return <IconComponent name={iconName} size={25} color={tintColor} />;
      },
    }),
    tabBarOptions: {
      activeTintColor: '#007ac1',
    },
  }
);

const AppContainer = createAppContainer(Navigator);

const App = () => {
  useEffect(() => {
    setTimeout(() => {
      SplashScreen.hide();
    }, 1000);
  }, []);

  return <AppContainer />;
};

export default App;

저는 이렇게 수정했습니다. Pure Functional Component를 사용하기 때문에 useEffect를 통해 처음 실행 시 1초 뒤 SplashScreen을 hide하는 코드를 작성했습니다. 저걸 네트워크 관련 코드로 처리해도 괜찮겠죠?

오늘은 이렇게 react-native-splash-screen을 사용하는법을 알아봤습니다.
읽어주셔서 감사합니다.

반응형