Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

미래학자

우버 클론 코딩 (nomad coders) #6 본문

nomad corders

우버 클론 코딩 (nomad coders) #6

미래학자 2019. 4. 15. 22:39

노마드 코더 - 우버 클론 코딩

#1.26 Planning the Resolvers part One

#1.27 Planning the Resolvers part Two

앞으로 할 일에 대해, 기능에 대해 정리

  • README.md

      # nuber-sever
    
      Server for the (N)UBER Clone Course on Nomad Academy. GraphQL, Typescript, NodeJS
    
      ### Public Resolvers:
    
      - [ ] Sigin In / Sign Up with Facebook
      - [ ] Sign In with Email
      - [ ] Start Phone Number Verification
      - [ ] Complete Phone Number Verification
      - [ ] Sign Up with Email
      --- 
    
      ### Private Resolvers:
    
      - [ ] Verify Email
      - [ ] Get my Profile
      - [ ] Update my Profile
      - [ ] Toggle Driving Mode
      - [ ] Report Location / Orientation
      - [ ] Add Place
      - [ ] Edit Place
      - [ ] Delete Place
      - [ ] See Nearby Drivers
      - [ ] Subscribe to Nearby Drivers
      - [ ] Request a Ride
      - [ ] Get Nearby Rides
      - [ ] Subscribe to Nearby Ride Requests
      - [ ] Subscribe to Ride Status
      - [ ] Get Chat Room Messages
      - [ ] Subscribe to Chat Room Messages
      - [ ] Send a Chat Message
    
      ## Code Chaalange
    
      - [ ] Get Ride History
      - [ ] See Ride Detail

#1.28 FacebookConnect Resolver part One

페이스북으로 사용자 인증을 하기 위한 작업을 한다. 이번에는 Mutation을 만들 것인데, Query get 처럼 가져오는 요청이면, Mutation은 post처럼 업데이트 또는 인서트 처럼 조작하는 것이라 생각하면 된다.

  • src/api/User/shared/User.graphql 에 fbId 필드를 추가하자.

          ...
        lastOrientation: Float
        fbId: String
      }
  • src/entities/User.ts 에도 fbId에 대한 필드를 추가하자. 이 값은 필수가 아니므로 nullable: true

          ...
          @Column({ type: "double precision", default:0})
        lastOrientation: number;
    
        @Column({ type: "text", nullable: true})
        fbId: string;
    
        public comparePassword(password: string): Promise<boolean> {
          return bcrypt.compare(password, this.password);
        }
          ...
  • src/api/User/facebookConnect/facebookConnect.graphql 을 생성해서 다음을 추가 하자.

      type FacebookConnectResponse{
        ok: Boolean!
        error: String
        token: String
      }
    
      type Mutation {
        FacebookConnect(
          firstName: String!, 
          lastName: String!, 
          email: String, 
          fbId: String!
        ) : FacebookConnectResponse!
      }

정의 후 yarn dev를 실행하면 src/types/graph.d.ts 에 정의한 타입이 생성되었을 것이다. 중간 중간에 graphql 파일을 정의하면 어떤 내용이 graph.d.ts 파일에 추가되는지 확인하면 좋을 것이다.

#1.28 FacebookConnect Resolver part Two

위에서 정의한 타입들에 대한 resolver를 정의할 차례다. graphql에서 요청 타입을 만들고 해당 응답을 하기 위한 resolver를 정의해야 응답이 된다.

  • src/entiries/User.ts 하기전에 우선 간단하게 필드 age 필드에 nullable을 넣어주자.

      @Column({ type: "int", nullable: true})
        age: number;

어떤 유저가 fbId를 통해 로그인 시도를 하게되고, 이미 해당 fbId가 디비에 있으면 이미 회원가입을 했다고 생각하고 로그인 토큰을 준다. 그러나 존재하지 않는 fbId면 회원 가입 처리가 진행된다.

아래에서 첫 번째 try 문은 이미 회원가입을 했는지에 대한 처리고, 두 번째는 신규 회원 가입 처리가 되는 try문이다.

  • src/api/User/facebookConnect/facebookConnect.resolvers.ts 파일을 생성하고 resolver를 정의한다.

      import { Resolvers } from "src/types/resolvers";
      import { FacebookConnectMutationArgs, FacebookConnectResponse } from "src/types/graph";
      import User from "../../../entities/User"; // 이건 꼭 상대경로로 해야 찾을 수 있다.
    
      const resolvers: Resolvers = {
        Mutation: {
          FacebookConnect: async (
            _, 
            args: FacebookConnectMutationArgs
          ) : Promise<FacebookConnectResponse> => {
            const { fbId } = args;
            try {
              const exitingUser = User.findOne({ fbId })
              if(exitingUser) {
                return {
                  ok: true,
                  error: null,
                  token: 'Comming Soon, existing'
                }
              }
            } catch (error) {
              return {
                ok: false,
                error: error.message,
                token: null
              }
            }
            try {
    
            } catch (error) {
              return {
                ok: false,
                error: error.message,
                token: null
              }
            }
          }
        }
      }
    
      export default resolvers;

#1.30 FacebookConnect Resolver part Three

저번 강의에 이어

  • src/api/User/FacebookConnect.d.ts 파일에서 현재 비어있는 두 번째 try 문 안을 채워주자.

      ...
                  try {
              await User.create({
                ...args,
                profilePhoto: `http://graph.facebook.com/${fbId}/picture?type=square'`
              }).save();
              return {
                ok: true,
                error: null,
                token: "Comming soon, create"
              }
            } catch (error) {
              return {
                ok: false,
                error: error.message,
                token: null
              }
            }
      ...

args에 넘어온 사용자 정보와 추가적으로 사용자의 페이스북 Image를 가져오도록 했다. 이 때, User.create(){}.save()을 하면 저번에 정의한 @beforeinsert 로직을 타서 password를 알아서 해싱하여 해싱한 값을 저장하게 한다.!

#1.31 Testing the FacebookConnect Resolver

이전 강의에서 설정한 Mutation으로 요청을 보내보자.

  • http://localhost:4000/playground 에 들어가서 쿼리를 날려보자.

      mutation {
        FacebookConnect(firstName: "TaeMin", lastName:"Moon", fbId: "2131321321", email: "test@gmail.com") {
          ok
          error
          token
        }
      }

아마 nullable 값 관련해서 문제가 발생할 것이다.

  • src/entities/User.ts email, password, phoneNumber 를 nullable로 바꾸자

      ...
          @Column({ type: "text", nullable: true})
        @IsEmail()
        email: string | null;
      ...
          @Column({ type: "text", nullable: true})
        password: string;
    
        @Column({ type: "text", nullable: true})
        phoneNumber: string;
      ...

이렇게 하고 다시 접속하면 정상적으로 Mutaion을 날리고 응답을 받을 것이다.

#1.32 EmailSignIn Resolver part One

이번에는 이메일로 로그인하는 것을 해볼 것이다.

  • src/api/User/EmailSignIn/EmailSignIn.graphql 파일을 생성하여 타입을 정의하자.

      type EmailSignInResponse {
        ok: Boolean!
        error: String
        token: String
      }
    type Mutation {
      EmailSignIn(email: String!, password: String!): EmailSignInResponse!
    }
  • src/api/User/EmailSignIn/EmailSignIn.resolvers.ts 파일을 생성하여 resolver를 정의하자.

    이메일과 비밀번호를 통해 로그인을 시도 하게 되어 있고, 해당 유저가 디비에 없다면 로그인은 실패한다. 유저가 있다면 비밀번호가 일치하는지 확인하게 된다.

      import { EmailSignInMutationArgs, EmailSignInResponse } from 'src/types/graph';
      import { Resolvers } from 'src/types/resolvers';
      import User from '../../../entities/User';
    
      const resolvers: Resolvers = {
        Mutation: {
          EmailSignIn: async(_, args: EmailSignInMutationArgs) :Promise<EmailSignInResponse> => {
            try {
              const { email } = args;
              const user =  User.findOne({ email });
              if(!user) {
                return {
                  ok: false,
                  error: "No User found with that email",
                  token: null
                }
              } else {// 임시 리턴 값
                return {
                  ok: true,
                  error: 'temp',
                  token: ''
                }
              }
            } catch (error) {
              return {
                ok: false,
                error: error.message,
                token: null
              }
            }
          }
        }
      }
    
      export default resolvers;

#1.32 EmailSignIn Resolver part Two

이메일로 로그인을 마무리 하자.

  • src/api/User/EmailSignIn/EmailSignIn.resolvers.ts

      import { EmailSignInMutationArgs, EmailSignInResponse } from 'src/types/graph';
      import { Resolvers } from 'src/types/resolvers';
      import User from '../../../entities/User';
    
      const resolvers: Resolvers = {
        Mutation: {
          EmailSignIn: async(_, args: EmailSignInMutationArgs) :Promise<EmailSignInResponse> => {
            try {
              const { email, password } = args;
              const user =  await User.findOne({ email });
              if(!user) {
                return {
                  ok: false,
                  error: "No User found with that email",
                  token: null
                }
              }
              const checkPassword = await user.comparePassword(password);
              if(checkPassword) {
                return {
                  ok: true,
                  error: null,
                  token: 'Comming soon'
                }
              } else {
                return {
                  ok: false,
                  error: "Wrong password",
                  token: null
                }
              }
            } catch (error) {
              return {
                ok: false,
                error: error.message,
                token: null
              }
            }
          }
        }
      }
    
      export default resolvers;

실제 기능은 아직 테스트 할 수 없다.

  • README.md 이제 우린 우리가 할 일 중 2가지를 체킹할 수 있다.

      ...
      - [X] Sigin In / Sign Up with Facebook
      - [X] Sign In with Email
      ...
Comments