스포티파이 클론 코딩 (1)을 보지 않은 분들은 (1)부터 보고 오시면 좋을 것 같습니다.
https://velog.io/@digyrh456789/Spotify-Clone-Coding-1
저번 시간에 헤더랑 메인페이지까지 만들었었다. 이번엔 푸터랑 로그인 기능을 만들어 볼 것이다.
import '../../styles/Footer/Footer.scss'
import { useNavigate } from 'react-router-dom'
export default function Footer() {
const nav = useNavigate();
function footerlog() {
if(window.location == "http://localhost:3000" || window.location == "http://localhost:3000/#"){
window.scrollTo({
top: 0,
behavior: 'smooth'
})
}
else {
nav('/');
window.scrollTo({
top: 0,
behavior: 'smooth'
})
}
}
function goinsta() {
window.location.href = 'https://www.instagram.com/spotify/'
}
function gotwit() {
window.location.href = 'https://twitter.com/spotify'
}
function goface() {
window.location.href = 'https://www.facebook.com/Spotify'
}
return (
<div className='footers'>
<img src='images/logo.png' className="spolog" onClick={footerlog}></img>
<div className='company'>
<p className='comtext'>회사</p>
<div className='declick'>
<p>상세정보</p>
<p>채용 정보</p>
<p>For the Record</p>
</div>
</div>
<div className='community'>
<p className='communitytext'>커뮤니티</p>
<div className='declick'>
<p>아티스트</p>
<p>개발자</p>
<p>투자자</p>
<p>공급업체</p>
</div>
</div>
<div className='link'>
<p className='linktext'>유용한 링크</p>
<div className='declick'>
<p>지원</p>
<p>앱 플레이어</p>
</div>
</div>
<div className='wowz'>
<div className='instabox'>
<img src="images/insta.png" className='instaimg' onClick={goinsta}></img>
</div>
<div className='twitterbox'>
<img src="images/twitter.png" className='twimg' onClick={gotwit}></img>
</div>
<div className='facebox'>
<img src="images/facebook.png" className='faceimg' onClick={goface}></img>
</div>
</div>
<div className='terms'>
<p>
<span className='declick'>
<span>법률 정보</span>
<span className='private'>개인정보 보호 센터</span>
<span className='private2'>개인정보 처리방침</span>
</span>
<span className='copyright'>© 2022 Spotify AB</span>
</p>
</div>
</div>
)
}
다음은 Footer.js의 코드이다. 코드 설명을 해보자면
footerlog()는 만약 spotify 메인화면에 있다면 footer에 있는 spotify로고를 눌렀을 때 화면 맨 위로 새로고침 하듯이 올라가게 해준다. 로그인을 하면 뒤에 #이 붙기 때문에 두개의 경우의 수를 설정해 주었다. 그리고 메인화면이 아닐시에는 메인화면으로 이동시켜준다.
goinsta() gotwit() goface() 함수는 각각 인스타그램 이미지, 트위터 이미지, 페이스북 이미지를 클릭했을 때, Spotify의 SNS로 이동시켜준다.
.footers {
bottom: 0px;
background-color: black;
width: 100%;
height: 600px;
color: white;
}
.spolog {
position: absolute;
width: 150px;
margin-top: 120px;
margin-left: -550px;
cursor: pointer;
}
.company {
position: absolute;
margin-left: 35%;
margin-top: 6%;
text-align: left;
white-space: nowrap;
.comtext {
font-size: 15px;
color: gray;
}
}
.community {
position: absolute;
margin-left: 45%;
margin-top: 6%;
text-align: left;
white-space: nowrap;
.communitytext {
font-size: 15px;
color: gray;
}
}
.link {
position: absolute;
margin-left: 55%;
margin-top: 6%;
text-align: left;
white-space: nowrap;
.linktext {
font-size: 15px;
color: gray;
}
}
.wowz {
.instabox {
position: absolute;
width: 70px;
height: 70px;
background-color: #333333;
border-radius: 50px;
margin-left: 70%;
margin-top: 7%;
.instaimg {
margin-top: 10px;
width: 50px;
cursor: pointer;
}
}
:hover {
background-color: #1ed760;
}
}
.twitterbox {
position: absolute;
width: 70px;
height: 70px;
background-color: #333333;
border-radius: 50px;
margin-left: 75%;
margin-top: 7%;
.twimg {
margin-top: 12px;
width: 50px;
cursor: pointer;
}
}
.facebox {
position: absolute;
width: 70px;
height: 70px;
background-color: #333333;
border-radius: 50px;
margin-left: 80%;
margin-top: 7%;
.faceimg {
margin-top: 10px;
width: 50px;
cursor: pointer;
}
}
.terms {
position: absolute;
margin-left: 20%;
margin-top: 25%;
font-size: 13px;
color: gray;
.private {
margin-left: 30px;
}
.private2 {
margin-left: 30px;
}
.copyright {
margin-left: 700px;
}
}
.declick {
:hover {
color: #1ed760;
cursor: pointer;
}
}
다음은 Footer.scss의 코드이다.
회사, 커뮤니티, 유용한 링크 탭들의 밑에 있는 텍스트위에 마우스를 올려놓으면 텍스트가 초록색으로 변하고, SNS이미지 위에 마우스를 올려놓아도 초록색으로 변한다.
다음은 완성된 푸터 화면이다.
이렇게 하면 메인화면은 얼추 다 만들어졌다고 할 수 있다.
다음은 로그인 기능을 한 번 만들어보겠다.
로그인버튼이 헤더에 있으니 헤더의 코드를 한 번 수정해주겠다.
import '../../styles/Header/Header.scss'
import React from 'react';
import axios from 'axios';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'
export default function Header() {
let nav = useNavigate();
const [token, setToken] = useState("")
const [searchKey, setSearchKey] = useState("")
const [artists, setArtists] = useState([])
const [profile, setProfile] = useState("");
const [username, setUsername] = useState("");
const CLIENT_ID = "++++++++++++++++++++"
const REDIRECT_URI = "http://localhost:3000"
const AUTH_ENDPOINT = "https://accounts.spotify.com/authorize"
const RESPONSE_TYPE = "token"
function goLogins() {
window.location.href = `${AUTH_ENDPOINT}?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=${RESPONSE_TYPE}`;
}
function goSign() {
window.location.href = 'https://www.spotify.com/kr-ko/signup';
}
useEffect(() => {
const hash = window.location.hash
let token = window.localStorage.getItem("token")
if (!token && hash) {
token = hash.substring(1).split("&").find(elem => elem.startsWith("access_token")).split("=")[1]
window.location.hash = ""
window.localStorage.setItem("token", token)
const res = axios.get("https://api.spotify.com/v1/me", {
headers: {
Authorization: `Bearer ${token}`
},
params: {
}
})
res.then(response => setUsername(response.data.display_name));
res.then(response => setProfile(response.data.images[0].url));
}
console.log(username);
console.log(profile);
setToken(token)
}, [])
function Logout() {
setToken(!token)
window.localStorage.removeItem("token")
window.location.replace("/")
}
function headerclick() {
window.location.replace("/")
}
function gopre() {
nav('/premium');
}
function down() {
nav('/download');
}
return (
<div className='mainheader'>
<div className='spotifyheader'>
<img src='images/logo.png' className='spotifylogo' onClick={headerclick}></img>
<div className='headtext'>
<text onClick={gopre}>프리미엄</text>
<text>지원</text>
<text onClick={down}>다운로드 하기</text>
<text className='bar'>|</text>
{!token ?
<text onClick={goSign}>회원가입하기</text>
: <span className='users'>
<img src={profile} className='userimg'></img>
<text>{username}님</text>
</span>}
{!token ?
<text onClick={goLogins}>로그인하기</text>
: <text onClick={Logout}>로그아웃</text>}
</div>
</div>
</div>
)
}
로그인 버튼을 누르면 goLogins()라는 함수가 작동하도록 해 두었다.
window.location.href를 이용하여 로그인 페이지로 이동하게 해야하는데, 로그인 페이지를 개발하는 사람이 직접 만들어야 한다.
https://developer.spotify.com/
Spotify for Developers사이트에서 Dashboard에 들어가준다.
Spotify게정이 있다면 로그인을 하고, 없으면 회원가입을 해주면 된다.
나는 이미 하나를 만들어서 저렇게 초록색으로 있지만, 만든 적이 없다면 CREATE AN APP를 누른다.
App name과 App description
말 그대로 앱 이름과 앱 설명이니 하고싶은대로 쓰면 된다.
그렇게 만들면 이런식으로 화면이 나오게 된다.
여기서 끝이면 좋겠지만 아쉽게도 마지막으로 해줘야할 것이 하나 남았다.
EDIT SETTINGS를 누른다.
그럼 뭐 이런식으로 화면이 나올텐데 우리가 여기서 해줄 것은 단 하나.
나는 로컬에서 돌릴것이므로, Redirect Urls에 http://localhost:3000를 추가해주어야 했다.
그렇게 로컬호스트 주소까지 추가했다면, 준비가 끝났다.
자동으로 로그인 페이지가 만들어졌을 것이다.
const CLIENT_ID = "++++++++++++++++++++"
const REDIRECT_URI = "http://localhost:3000"
const AUTH_ENDPOINT = "https://accounts.spotify.com/authorize"
const RESPONSE_TYPE = "token"
function goLogins() {
window.location.href = `${AUTH_ENDPOINT}?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=${RESPONSE_TYPE}`;
}
이것이 로그인하는 코드인데, CLIENT_ID에 자신이 만든 대시보드에 있는 CLIENTID를 +++++++++++++++++칸에 넣어주면 된다.
useEffect(() => {
const hash = window.location.hash
let token = window.localStorage.getItem("token")
if (!token && hash) {
token = hash.substring(1).split("&").find(elem => elem.startsWith("access_token")).split("=")[1]
window.location.hash = ""
window.localStorage.setItem("token", token)
const res = axios.get("https://api.spotify.com/v1/me", {
headers: {
Authorization: `Bearer ${token}`
},
params: {
}
})
res.then(response => setUsername(response.data.display_name));
res.then(response => setProfile(response.data.images[0].url));
}
console.log(username);
console.log(profile);
setToken(token)
}, [])
그리고 useEffect를 작성해주었다.
실행하면 hashlocalStorage에 토큰이 있는지 또는 이미 저장된 토큰이 있는지 확인한다.
토큰이 저장되어 있으면 계속 진행하며 없으면 해시가 있는지 확인한다.
그런다음, 토큰을 추출한다.
처음에 해시를 서브스트링하고 문자열을 앰퍼샌드로 나눈다.
그런 다음 access_token을 포함하는 것들을 찾고, 다시 등호에서 분할시킨다.
그렇게 얻은 배열에는 인덱스 1에 토큰이 포함되어 있다.
마지막으로
토큰을 받으면 localStorage에 저장하고 해시를 재설정한다.
이렇게 까지 해주면, 로그인 할 준비는 다 끝난다.
하지만 로그인만 하면 로그인이 되었는지를 잘 모른다.
{!token ?
<text onClick={goSign}>회원가입하기</text>
: <span className='users'>
<img src={profile} className='userimg'></img>
<text>{username}님</text>
</span>}
{!token ?
<text onClick={goLogins}>로그인하기</text>
: <text onClick={Logout}>로그아웃</text>}
Header.js의 코드인데, 삼항연산자를 이용하여 토큰이 없으면 회원가입하기와 로그인하기가 나오게하고, 토큰이 있다면 유저의 프로필을 가져와 유저의 프로필 사진과 이름, 로그아웃 버튼으로 바뀌게 해주었다.
그럼 유저의 프로필과 이름은 어떻게 가져오는가?
const res = axios.get("https://api.spotify.com/v1/me", {
headers: {
Authorization: `Bearer ${token}`
},
params: {
}
})
res.then(response => setUsername(response.data.display_name));
res.then(response => setProfile(response.data.images[0].url));
다음과 같은 방식으로 가져와 주었다.
Spotify공식 홈페이지를 참고하였으며 로그인한 유저 정보를 가져오러면
https://api.spotify.com/v1/me
에서 get를 이용해 받아와야 한다고 한다.
그리고 토큰을 이용해 인증해 주었다.
그렇게 로그인을 하게 되면 res라는 변수에 유저의 정보가 담기게 된다.
res를 console.log로 출력해보면
다음과 같이 Promise로 나온다.
PromiseResult안에 있는 data에 유저 정보가 있으므로 저곳에서 정보를 가져올 것이다.
PromiseResult에 접근해 주기 위해서,
res.then(response => (response.data));
이렇게 해줘서 콘솔로 찍어보면,
위 Promise가 그냥 res를 콘솔로 찍은 것이며 아래가 res.then을 해준것이다.
display_name이 Spotify이름이기 때문에,
res.then코드의 뒤에 있는 response.data에 display_name만 추가해준다.
추가하고 콘솔로 찍으면 성공적으로 나올것이다.
그런다음, 이미지를 가져와보자.
images는 딱 보면 배열인 것을 알 수 있다.
배열의 0번째에 이미지의 url이 담겨있으므로 가져오자.
마찬가지로 response.data뒤에 추가만 해주면 된다.
images의 0번째 배열의 url에 값이 있으니
images[0].url을 추가해서 가져와주면 된다.
그렇게 가져온것은 말 그대로 url주소 이므로
<img src={profile}>
이런식으로 useState를 활용한 변수에 url를 저장한 후 src로 불러와주었다.
이렇게 하면 로그인을 했을 때
헤더가 성공적으로 변경되는 것을 볼 수 있다.
오늘은 이렇게 로그인 기능까지 만들어보았다. 다음은 헤더에 프리미엄 버튼을 누르면 이동되는 프리미엄 페이지를 만들어보도록 하겠다.