React Apollo Hooks를 사용하여 로그인 기능을 만들었습니다.
소개
react-apollo-hooks
가 편리했기 때문에 SPA
로그인 기능을 만들면서 사용법을 소개합니다.
실제로 작성한 프로젝트는 Github에 올립니다.
이번에도 프로젝트의 병아리는 create-react-app
로 작성합니다.
TypeScript
에서 만들었으므로 명령은 다음과 같습니다.
$ create-react-app sample-login-with-react-apollo-hooks --typescript
※백엔드(API 서버)에 대해서 소개는 포함하고 있지 않습니다.
버전
패키지
버전
typescript
3.5.3
react
16.9.0
react-apollo
3.0.1
react-apollo-hooks
0.5.0
graphql
14.4.2
graphql-tag
2.10.1
react-router-dom
5.0.1
이번에 만드는 기능
$ create-react-app sample-login-with-react-apollo-hooks --typescript
패키지
버전
typescript
3.5.3
react
16.9.0
react-apollo
3.0.1
react-apollo-hooks
0.5.0
graphql
14.4.2
graphql-tag
2.10.1
react-router-dom
5.0.1
이번에 만드는 기능
localStorage
에 저장됩니다. localStorage
에 저장된 토큰으로 문의하여 로그인되었는지 확인합니다. 화면 준비
로그인 페이지와 루트 페이지를 준비합니다.
src/Login/index.tsximport React from "react";
const Login = () => {
return (
<div>
<div>
<label>
ID
<br />
<input type="text" />
</label>
</div>
<div>
<label>
PW
<br />
<input type="password" />
</label>
</div>
<div>
<input type="submit" value="ログイン" />
</div>
</div>
);
};
export default Login;
src/Home/index.tsximport React from "react";
const Home = () => {
return <div>Home</div>;
};
export default Home;
루트 준비
src/index.tsximport React from "react";
import { Route, Switch } from "react-router-dom";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import * as serviceWorker from "./serviceWorker";
import Home from "./Home";
import Login from "./Login";
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
</Switch>
</BrowserRouter>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
http://localhost:3000/login 방문
http://localhost:3000/ 방문
페이지와 루트를 준비했습니다.
react-apollo 준비
client
를 작성합니다.
src/index.tsx import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import * as serviceWorker from "./serviceWorker";
+import { ApolloProvider } from "react-apollo-hooks";
+import { InMemoryCache } from "apollo-cache-inmemory";
+import { ApolloClient } from "apollo-client";
+import { setContext } from "apollo-link-context";
+import { createHttpLink } from "apollo-link-http";
+import { Route, Switch } from "react-router-dom";
import Home from "./Home";
import Login from "./Login";
+const uri = "http://localhost:5000/graphql";
+const context = setContext((_, { headers }) => {
+ return {
+ headers: { ...headers, token: localStorage.getItem("token") }
+ };
+});
+const link = context.concat(createHttpLink({ uri }));
+const cache = new InMemoryCache();
+const client = new ApolloClient({ cache, link });
ReactDOM.render(
<BrowserRouter>
+ <ApolloProvider client={client}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
</Switch>
+ </ApolloProvider>
</BrowserRouter>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
uri
에는 백엔드 엔드포인트를 지정하십시오.
요청 헤더에 token
를 localStorage
에서 가져와서 설정합니다.
이번은 Rails + API + GraphQL
의 프로젝트를 5000
번 포트로 기동하고 있었습니다.
react-apollo-hooks
를 사용할 준비가 되었으므로 즉시 로그인 기능으로 만들어 갑시다.
로그인 기능
아래와 같은 Mutation
를 백엔드측에 준비해 둡니다.
mutation {
login(input: {loginid: "loginid", password: "password"}) {
user {
id
accessToken {
token
}
}
result
}
}
여기에 로그인 양식에 입력된 값을 포함 요청을 보내고 싶습니다.
입력 값을 State에 저장
src/Login/index.tsx-import React from "react";
+import React, { useState } from "react";
...
const Login = () => {
+ const [loginid, setLoginid] = useState("");
+ const [password, setPassword] = useState("");
...
-<input type="text" />
+<input
+ type="text"
+ value={loginid}
+ onChange={e => setLoginid(e.target.value || "")}
+/>
...
-<input type="text" />
+<input
+ type="password"
+ value={password}
+ onChange={e => setPassword(e.target.value || "")}
+/>
...
로그인 버튼을 로그인 처리 실행
src/Login/index.tsx import React, { useState } from "react";
+import { useMutation } from "react-apollo-hooks";
+import { withRouter } from "react-router";
+import { History } from "history";
+import gql from "graphql-tag";
+interface IProps {
+ history: History;
+}
+const Login = ({ history }: IProps) => {
...
+ const loginMutation = gql`
+ mutation login($loginid: String!, $password: String!) {
+ login(input: { loginid: $loginid, password: $password }) {
+ user {
+ accessToken {
+ token
+ }
+ }
+ result
+ }
+ }
+ `;
+
+ const [login] = useMutation(loginMutation, {
+ update: (_proxy, response) => {
+ if (response.data.login.result) {
+ localStorage.setItem(
+ "token",
+ response.data.login.user.accessToken.token
+ );
+ history.push("/");
+ } else {
+ alert("ログイン情報が不正です。");
+ setLoginid("");
+ setPassword("");
+ }
+ },
+ variables: { loginid, password }
+ });
return (
...
-<input type="submit" value="ログイン" />
+<input type="submit" value="ログイン" onClick={() => login()} />
...
-export default Login;
+export default withRouter(Login);
로그인에 성공( response.data.login.result
하지만 true
) 하면 루트 페이지로 이동해, 실패하면 alert
를 내는 처리가 할 수 있었습니다.
로그인에 성공하면 응답에서 얻은 토큰을 localStorage
로 설정합니다.
localStorage
에 토큰을 세트하는 처리가 생겼으므로, 이미 로그인된 경우 루트 페이지로 리디렉션하는 처리를 만듭니다.
로그인한 사용자의 루트 페이지로 자동 리디렉션 처리
다음과 같은 로그인된 사용자를 얻는 Query
를 백엔드측에 준비해 둡니다.
{
loggedUser {
id
}
}
응답에 사용자 정보가 포함되어 있으면 로그인되어 있고 null
이면 로그인되지 않은 것으로 판단합니다.
src/Login/index.tsx...
-import { withRouter } from "react-router";
+import { Redirect, withRouter } from "react-router";
...
-import { useMutation } from "react-apollo-hooks";
+import { useQuery, useMutation } from "react-apollo-hooks";
...
+ const loggedUserQuery = gql`
+ {
+ loggedUser {
+ id
+ }
+ }
+ `;
const loginMutation = gql`
...
const [login] = useMutation(loginMutation, {
...
+ const { loading, data } = useQuery(loggedUserQuery);
+ if (loading) {
+ return <div>Now Loading...</div>;
+ } else if (data.loggedUser) {
+ return <Redirect to="/" />;
+ }
const login = () => {
...
로그인하고 있는지 문의중에는 로딩 메시지를 표시해, 로그인하고 있으면, 루트 페이지로 리디렉트, 그렇지 않으면, 로그인 페이지를 표시할 수 있게 되었습니다.
마지막으로
/graphiql 에서 쿼리를 시도 할 때 프론트 측에서 정의한 쿼리를 복사하기만으로 실행할 수 있기 때문에 기쁩니다.
※인수가 있는 경우는 variables
를 화면 좌하의 폼으로 설정할 필요가 있습니다.
참고문헌
import React from "react";
const Login = () => {
return (
<div>
<div>
<label>
ID
<br />
<input type="text" />
</label>
</div>
<div>
<label>
PW
<br />
<input type="password" />
</label>
</div>
<div>
<input type="submit" value="ログイン" />
</div>
</div>
);
};
export default Login;
import React from "react";
const Home = () => {
return <div>Home</div>;
};
export default Home;
src/index.tsx
import React from "react";
import { Route, Switch } from "react-router-dom";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import * as serviceWorker from "./serviceWorker";
import Home from "./Home";
import Login from "./Login";
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
</Switch>
</BrowserRouter>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
http://localhost:3000/login 방문
http://localhost:3000/ 방문
페이지와 루트를 준비했습니다.
react-apollo 준비
client
를 작성합니다.
src/index.tsx import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import * as serviceWorker from "./serviceWorker";
+import { ApolloProvider } from "react-apollo-hooks";
+import { InMemoryCache } from "apollo-cache-inmemory";
+import { ApolloClient } from "apollo-client";
+import { setContext } from "apollo-link-context";
+import { createHttpLink } from "apollo-link-http";
+import { Route, Switch } from "react-router-dom";
import Home from "./Home";
import Login from "./Login";
+const uri = "http://localhost:5000/graphql";
+const context = setContext((_, { headers }) => {
+ return {
+ headers: { ...headers, token: localStorage.getItem("token") }
+ };
+});
+const link = context.concat(createHttpLink({ uri }));
+const cache = new InMemoryCache();
+const client = new ApolloClient({ cache, link });
ReactDOM.render(
<BrowserRouter>
+ <ApolloProvider client={client}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
</Switch>
+ </ApolloProvider>
</BrowserRouter>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
uri
에는 백엔드 엔드포인트를 지정하십시오.
요청 헤더에 token
를 localStorage
에서 가져와서 설정합니다.
이번은 Rails + API + GraphQL
의 프로젝트를 5000
번 포트로 기동하고 있었습니다.
react-apollo-hooks
를 사용할 준비가 되었으므로 즉시 로그인 기능으로 만들어 갑시다.
로그인 기능
아래와 같은 Mutation
를 백엔드측에 준비해 둡니다.
mutation {
login(input: {loginid: "loginid", password: "password"}) {
user {
id
accessToken {
token
}
}
result
}
}
여기에 로그인 양식에 입력된 값을 포함 요청을 보내고 싶습니다.
입력 값을 State에 저장
src/Login/index.tsx-import React from "react";
+import React, { useState } from "react";
...
const Login = () => {
+ const [loginid, setLoginid] = useState("");
+ const [password, setPassword] = useState("");
...
-<input type="text" />
+<input
+ type="text"
+ value={loginid}
+ onChange={e => setLoginid(e.target.value || "")}
+/>
...
-<input type="text" />
+<input
+ type="password"
+ value={password}
+ onChange={e => setPassword(e.target.value || "")}
+/>
...
로그인 버튼을 로그인 처리 실행
src/Login/index.tsx import React, { useState } from "react";
+import { useMutation } from "react-apollo-hooks";
+import { withRouter } from "react-router";
+import { History } from "history";
+import gql from "graphql-tag";
+interface IProps {
+ history: History;
+}
+const Login = ({ history }: IProps) => {
...
+ const loginMutation = gql`
+ mutation login($loginid: String!, $password: String!) {
+ login(input: { loginid: $loginid, password: $password }) {
+ user {
+ accessToken {
+ token
+ }
+ }
+ result
+ }
+ }
+ `;
+
+ const [login] = useMutation(loginMutation, {
+ update: (_proxy, response) => {
+ if (response.data.login.result) {
+ localStorage.setItem(
+ "token",
+ response.data.login.user.accessToken.token
+ );
+ history.push("/");
+ } else {
+ alert("ログイン情報が不正です。");
+ setLoginid("");
+ setPassword("");
+ }
+ },
+ variables: { loginid, password }
+ });
return (
...
-<input type="submit" value="ログイン" />
+<input type="submit" value="ログイン" onClick={() => login()} />
...
-export default Login;
+export default withRouter(Login);
로그인에 성공( response.data.login.result
하지만 true
) 하면 루트 페이지로 이동해, 실패하면 alert
를 내는 처리가 할 수 있었습니다.
로그인에 성공하면 응답에서 얻은 토큰을 localStorage
로 설정합니다.
localStorage
에 토큰을 세트하는 처리가 생겼으므로, 이미 로그인된 경우 루트 페이지로 리디렉션하는 처리를 만듭니다.
로그인한 사용자의 루트 페이지로 자동 리디렉션 처리
다음과 같은 로그인된 사용자를 얻는 Query
를 백엔드측에 준비해 둡니다.
{
loggedUser {
id
}
}
응답에 사용자 정보가 포함되어 있으면 로그인되어 있고 null
이면 로그인되지 않은 것으로 판단합니다.
src/Login/index.tsx...
-import { withRouter } from "react-router";
+import { Redirect, withRouter } from "react-router";
...
-import { useMutation } from "react-apollo-hooks";
+import { useQuery, useMutation } from "react-apollo-hooks";
...
+ const loggedUserQuery = gql`
+ {
+ loggedUser {
+ id
+ }
+ }
+ `;
const loginMutation = gql`
...
const [login] = useMutation(loginMutation, {
...
+ const { loading, data } = useQuery(loggedUserQuery);
+ if (loading) {
+ return <div>Now Loading...</div>;
+ } else if (data.loggedUser) {
+ return <Redirect to="/" />;
+ }
const login = () => {
...
로그인하고 있는지 문의중에는 로딩 메시지를 표시해, 로그인하고 있으면, 루트 페이지로 리디렉트, 그렇지 않으면, 로그인 페이지를 표시할 수 있게 되었습니다.
마지막으로
/graphiql 에서 쿼리를 시도 할 때 프론트 측에서 정의한 쿼리를 복사하기만으로 실행할 수 있기 때문에 기쁩니다.
※인수가 있는 경우는 variables
를 화면 좌하의 폼으로 설정할 필요가 있습니다.
참고문헌
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import * as serviceWorker from "./serviceWorker";
+import { ApolloProvider } from "react-apollo-hooks";
+import { InMemoryCache } from "apollo-cache-inmemory";
+import { ApolloClient } from "apollo-client";
+import { setContext } from "apollo-link-context";
+import { createHttpLink } from "apollo-link-http";
+import { Route, Switch } from "react-router-dom";
import Home from "./Home";
import Login from "./Login";
+const uri = "http://localhost:5000/graphql";
+const context = setContext((_, { headers }) => {
+ return {
+ headers: { ...headers, token: localStorage.getItem("token") }
+ };
+});
+const link = context.concat(createHttpLink({ uri }));
+const cache = new InMemoryCache();
+const client = new ApolloClient({ cache, link });
ReactDOM.render(
<BrowserRouter>
+ <ApolloProvider client={client}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/login" component={Login} />
</Switch>
+ </ApolloProvider>
</BrowserRouter>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
아래와 같은
Mutation
를 백엔드측에 준비해 둡니다.mutation {
login(input: {loginid: "loginid", password: "password"}) {
user {
id
accessToken {
token
}
}
result
}
}
여기에 로그인 양식에 입력된 값을 포함 요청을 보내고 싶습니다.
입력 값을 State에 저장
src/Login/index.tsx
-import React from "react";
+import React, { useState } from "react";
...
const Login = () => {
+ const [loginid, setLoginid] = useState("");
+ const [password, setPassword] = useState("");
...
-<input type="text" />
+<input
+ type="text"
+ value={loginid}
+ onChange={e => setLoginid(e.target.value || "")}
+/>
...
-<input type="text" />
+<input
+ type="password"
+ value={password}
+ onChange={e => setPassword(e.target.value || "")}
+/>
...
로그인 버튼을 로그인 처리 실행
src/Login/index.tsx
import React, { useState } from "react";
+import { useMutation } from "react-apollo-hooks";
+import { withRouter } from "react-router";
+import { History } from "history";
+import gql from "graphql-tag";
+interface IProps {
+ history: History;
+}
+const Login = ({ history }: IProps) => {
...
+ const loginMutation = gql`
+ mutation login($loginid: String!, $password: String!) {
+ login(input: { loginid: $loginid, password: $password }) {
+ user {
+ accessToken {
+ token
+ }
+ }
+ result
+ }
+ }
+ `;
+
+ const [login] = useMutation(loginMutation, {
+ update: (_proxy, response) => {
+ if (response.data.login.result) {
+ localStorage.setItem(
+ "token",
+ response.data.login.user.accessToken.token
+ );
+ history.push("/");
+ } else {
+ alert("ログイン情報が不正です。");
+ setLoginid("");
+ setPassword("");
+ }
+ },
+ variables: { loginid, password }
+ });
return (
...
-<input type="submit" value="ログイン" />
+<input type="submit" value="ログイン" onClick={() => login()} />
...
-export default Login;
+export default withRouter(Login);
로그인에 성공(
response.data.login.result
하지만 true
) 하면 루트 페이지로 이동해, 실패하면 alert
를 내는 처리가 할 수 있었습니다.로그인에 성공하면 응답에서 얻은 토큰을
localStorage
로 설정합니다.localStorage
에 토큰을 세트하는 처리가 생겼으므로, 이미 로그인된 경우 루트 페이지로 리디렉션하는 처리를 만듭니다.로그인한 사용자의 루트 페이지로 자동 리디렉션 처리
다음과 같은 로그인된 사용자를 얻는 Query
를 백엔드측에 준비해 둡니다.
{
loggedUser {
id
}
}
응답에 사용자 정보가 포함되어 있으면 로그인되어 있고 null
이면 로그인되지 않은 것으로 판단합니다.
src/Login/index.tsx...
-import { withRouter } from "react-router";
+import { Redirect, withRouter } from "react-router";
...
-import { useMutation } from "react-apollo-hooks";
+import { useQuery, useMutation } from "react-apollo-hooks";
...
+ const loggedUserQuery = gql`
+ {
+ loggedUser {
+ id
+ }
+ }
+ `;
const loginMutation = gql`
...
const [login] = useMutation(loginMutation, {
...
+ const { loading, data } = useQuery(loggedUserQuery);
+ if (loading) {
+ return <div>Now Loading...</div>;
+ } else if (data.loggedUser) {
+ return <Redirect to="/" />;
+ }
const login = () => {
...
로그인하고 있는지 문의중에는 로딩 메시지를 표시해, 로그인하고 있으면, 루트 페이지로 리디렉트, 그렇지 않으면, 로그인 페이지를 표시할 수 있게 되었습니다.
마지막으로
/graphiql 에서 쿼리를 시도 할 때 프론트 측에서 정의한 쿼리를 복사하기만으로 실행할 수 있기 때문에 기쁩니다.
※인수가 있는 경우는 variables
를 화면 좌하의 폼으로 설정할 필요가 있습니다.
참고문헌
{
loggedUser {
id
}
}
...
-import { withRouter } from "react-router";
+import { Redirect, withRouter } from "react-router";
...
-import { useMutation } from "react-apollo-hooks";
+import { useQuery, useMutation } from "react-apollo-hooks";
...
+ const loggedUserQuery = gql`
+ {
+ loggedUser {
+ id
+ }
+ }
+ `;
const loginMutation = gql`
...
const [login] = useMutation(loginMutation, {
...
+ const { loading, data } = useQuery(loggedUserQuery);
+ if (loading) {
+ return <div>Now Loading...</div>;
+ } else if (data.loggedUser) {
+ return <Redirect to="/" />;
+ }
const login = () => {
...
/graphiql 에서 쿼리를 시도 할 때 프론트 측에서 정의한 쿼리를 복사하기만으로 실행할 수 있기 때문에 기쁩니다.
※인수가 있는 경우는
variables
를 화면 좌하의 폼으로 설정할 필요가 있습니다.참고문헌
Reference
이 문제에 관하여(React Apollo Hooks를 사용하여 로그인 기능을 만들었습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/pure-adachi/items/980fc8ae644de70335f1텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)