반응형

이전에 팀프로젝트로 만든 파일을  로컬에서만 쓰다가 오늘 호스팅 하는 방법을 배워서

호스팅 하는 방법을 올려보려고 한다.

 

리액트파일과 서버파일이 있어서 두곳에 따로따로 호스팅 해줘야 한다. 

 

Node.js 서버 호스팅

HEROKU

https://dashboard.heroku.com/apps

 

Heroku

 

dashboard.heroku.com

위의 사이트에 들어가서 가입을 해줘야한다.

가입을 하면 로그인을 하고 create new app으로 들어가 준다

 

이제 여기서 이름을 설정해 주고 Create app을 누르면 깃허브리포지터리 같은 게 만들어진다.

 

만들고 나면 이런 창으로 이동하게 되는데 깃허브처럼 파일을 여기로 보내주면 된다

heroku로 push 하기 위해서는 heroku를 설치해줘야 한다. 

https://devcenter.heroku.com/articles/heroku-cli

 

The Heroku CLI | Heroku Dev Center

Last updated January 17, 2023 The Heroku Command Line Interface (CLI) lets you create and manage Heroku apps directly from the terminal. It’s an essential part of using Heroku. Install the Heroku CLI Pre-requisites The Heroku CLI requires Git, the popula

devcenter.heroku.com

링크로 들어가서 해당하는 운영체제와 옵션에 따라 설치해주면 된다.

 

설치하면 이제 호스팅 할준비가 끝났다 호스팅할 파일들도 준비를 좀 해줘 야한다

 

1. 호스팅 할 서버 파일이름을 index.js로 변경 

2. 호스팅 하는 파일의 port번호 변경

const port = process.env.PORT || 8080;
//지정해주는 포트번호가있다면 지정번호를사용하고 없다면 8080포트 사용

3.package.json 변경

 

 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },

 

이렇게 해준 뒤에 이제 깃허브에 push 하는 것과 똑같이 해주면 된다 

cmd 나 터미널을 이용해 주자

서버폴더위치에서 입력해줘야 한다.

 

주의사항*

서버를 깃허브에서 클론 해온 경우 node.module이 설치되어 있는지 확인 

없다면 npm install로 설치

 

. git 폴더를 지우고 처음부터 git init 하기 

 

업로드 전 서버를 실행시켜 보고 잘 실행되는지 확인!

 

json의 scripts.start 부분이 서버의 파일이름과 일치하는지 확인!!!

 

 

터미널에 입력

$ cd 프로젝트 위치
$ git init
$ git add .
$ git commit -m "heroku deploy" //커밋은 넣고싶은메모를 넣으면된다
$ heroku git:remote -a app name //이부분은 deploy에 쓰여있으니 복붙!
 --> 예시 $ heroku git:remote -a zzang9ha

$ git push heroku main //현재브랜치이름을 넣어주자 브랜치변경을 하지않았다면 master일수도 있다

 

push 까지 해 주고 오른쪽상단에 있는 open App을 눌러서 이런 창이 뜨면 성공이다 다른 에러창이 뜬다면 

json파일과 포트번호 그리고 모듈들이 잘 설치되어 있는지 다시 확인해 보고 리포지터리를 삭제한 다음 다시 만들어서 호스팅을 시도해야 한다..

 

서버의 데이터를 요청하는 주소로 이동하니 데이터를 잘 보내주는 걸 볼 수 있다! 이제 서버 끝 

 

 

React.js 호스팅

 

Vercel 가입하기

 

https://vercel.com/

 

Vercel: Develop. Preview. Ship. For the best frontend teams

Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.

vercel.com

위 사이트를 통해서 github를 연결해서 바로 호스팅 할 수 있다.

깃허브로 회원가입이 가능하니 깃허브로 가입하고 빠르게 진행할 수 있다.

 

vercel에 리액트 파일을 호스팅 하기 전에 리액트 파일의 서버주소를

호스팅 된 노드서버주소로 변경해 준 다음 npm start로 실행해 보자.

리액트프로젝트의 서버주소 변경

나는 서버주소를 apiurl.js에서 변수로 선언해서 사용하고 있어서 여기 주소만 변경해 주겠다. 뒤에 / 가 필요하면 붙여주고 필요 없으면 때 주면 된다 서버에 요청을 어떻게 하는지 확인하자.

 

이제 다시 vecel로 돌아가서 

 

Add new에 project를 클릭하고 들어가서

깃허브를 연결해 주고 호스팅 하고 싶은 파일을 import 해주면 된다.

이제 이 화면에서 Deploy 해주고 기다리면 호스팅이 끝난다.

 

여기서는 수정할 부분이 있으면 로컬파일을 수정해서 다시 깃허브에 push 해주면

vercel에서 다시 호스팅 해준다.

 

https://team-project-thenadlee.vercel.app/

 

React App

 

team-project-thenadlee.vercel.app

호스팅 된 팀프로젝트.

반응형
반응형

 

 
OMG
아티스트
NewJeans
앨범
NewJeans 'OMG'
발매일
2023.01.02

완성본링크

https://cokeholic-kim.github.io/VanillaJS-TODO/

 

Document

 

cokeholic-kim.github.io

이번에 노마드코더 자바스크립트 챌린지 강의를 자바스크립트 복습도 할 겸 도전하게 되었다. 정말 기초부터 시작해서 크게 어렵지 않게 따라 할 수 있고 다 배우면 요런 투두리스트사이트를 하나 만들 수 있다. 

 

코드를 한번 보자.

 

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <div id="login">
        <div>
            <h1 class="" id="greeting">Welcome</h1>
            <form class="" id="login-form">
                <input type="text" placeholder="이름을 알려주세요">
            </form>
        </div>
    </div>
    <header>
        <h2 id="clock">00:00:00</h2>
    </header>
    <div id="content">
        <form id="todo-form">
            <input type="text" placeholder="Write a To Do and press Enter">
        </form>
        <ul id="todo-list"></ul>
    </div>
    <footer>
        <div id="quote">
            <span>Daily advice :</span>
            <span></span>
        </div>
        <div id="weather">
            <span></span>
            <span></span>
        </div>
    </footer>
    <div id="background"></div>
</body>
<script src="src/login.js"></script>
<script src="src/timer.js"></script>
<script src="src/weather.js"></script>
<script src="src/todo.js"></script>
<script src="src/backgroundimg.js"></script>
<script src="src/advice.js"></script>
</html>

HTML에는 로그인을 할수있는 부분과 콘텐츠 부분 그리고 뒤에 보일 배경 div들을 만들어놓았다. 자바스크립트는 기능별로 파일을 만들어서 사용한다.

 

제일처음 login.js부분을 보자

 

login.js

const loginInput = document.querySelector("#login-form input")
const loginForm = document.querySelector("#login-form")
let UserId = ""
loginForm.addEventListener("submit",(e)=>{
    e.preventDefault();
    UserId = loginInput.value;
    document.querySelector("#login").classList.add('hidden')
    console.log(UserId)

    if(UserId){
        savedToDos = localStorage.getItem(UserId);
        if(savedToDos){
            const parsedTodos = JSON.parse(savedToDos)
            toDos = parsedTodos
            parsedTodos.forEach(newTodo => {
                paintTodo(newTodo)
            });
        }
    }
    
})

로그인폼에서 submit이벤트가 발생하면 창이 새로고침되는 이벤트를 막아주고

Input태그에 입력된값을 UserId 변수에 저장한다.

그 후 로그인창은 hidden클래스를 추가해서 안 보이게 숨겨준다.

UserId가 입력되었을때 로컬저장소에 UserId키로 저장한 값이 있다면

값을 받아와서 forEach구문으로 todolist에 뿌려준다

아래에 로컬스토리지 부분은 todolist 쪽에서 사용하는 기능인데

submit 될 때 다시 값을 읽어와야 해서 완전히 분리는 못했다.

localstorageAPI는 todolist구문에서 설명하겠다.

 

timer.js

const timeContainer = document.querySelector("#clock")

function timer(){
    const now = new Date()
    timeContainer.innerText = 
    `${now.getHours()}:${String(now.getMinutes()).padStart(2,"0")}:${String(now.getSeconds()).padStart(2,"0")}`
}
timer()
setInterval(timer,1000)

현재시간을 알려주는 함수 부분이다. new Date() 로 현재의 시간을 받고 아래에서 innerText로 

now.getHours() : 시간

now.getMinutes() : 분

now.getSeconds() : 초 

값들을 넣어준다. 여기서 padStart라는 메서드를 사용했는데 이 메서드로 원래 6 이렇게 한 개만 보이던 숫자를 06처럼 앞에 0을 붙여서 2글자로 만들어준다 이미 두 글자이면 같은 글자가나 온다 padStart는 문자열에만 사용가능하기 때문에 String으로 숫자를 문자열로 변경한 다음 사용해 줬다.

 

시간을 초가 바뀔 때마다 새로 그려줘 야하기 때문에 setInterval로 1초마다 새로 시간을 받아오고 그려주게 했다.

setInterval을 쓰면 1초가 지난 뒤에 함수가 실행되기 때문에 처음 창을 열 때 아무것도 실행되고 있지 않기 때문에 timer() 함수를 호출해서 한번 먼저 실행되게 해 줬다.

 

weather.js

const API_KEY = 'apikey'

async function onGeoOk(position){
    const lat = position.coords.latitude
    const lng = position.coords.longitude
    const API_URL = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lng}&appid=${API_KEY}&units=metric`
    
    fetch(API_URL).then(res => res.json()).then(data=>{
        const city = document.querySelector('#weather span:first-child')
        const weather = document.querySelector('#weather span:last-child')
        city.innerText = `${data.name} :`
        weather.innerText = `${data.weather[0].main} / temp ${data.main.temp}`
    })
}
function onGeoError(){
    alert("can't find you")
}

navigator.geolocation.getCurrentPosition(onGeoOk,onGeoError)

이 구문은 openWeathermapAPI를 사용하는 구문으로 현재위치를 받아서 API에서 현재위치의 기온 위치이름 날씨를 받아온다.

 

현재위치는

navigator.geolocation.getCurrentPosition(성공했을 때실행될 함수, 실패할 때실행될 함수)로 받아올 수 있다.

현재위치의 위도경도를 저장하고 API_URL문자열에 삽입해서 fetch로 자료를 요청해 받아 온다음

자료를 html에 넣어준다.

 

todo.js

const toDoForm = document.getElementById("todo-form")
const toDoInput = toDoForm.querySelector("input")
const toDoList = document.getElementById("todo-list")

let toDos = [];
let savedToDos

function saveTodos(){
    localStorage.setItem(UserId,JSON.stringify(toDos))
}

function deleteToDo(e){
    const li = e.target.parentElement
    li.remove()
    toDos = toDos.filter(toDo => toDo.id !== Number(li.id));
    saveTodos()
}

function paintTodo(newTodoObj){
    const li = document.createElement('li')
    li.id = newTodoObj.id
    const span = document.createElement('span')
    const button = document.createElement('button')
    button.innerText = "❌"
    button.addEventListener('click',deleteToDo)
    li.appendChild(span);
    li.appendChild(button);
    span.innerText = newTodoObj.text;
    toDoList.appendChild(li)
}

function handleTodoSubmit(e){
    e.preventDefault()
    const newTodo = toDoInput.value
    toDoInput.value = ""
    const newTodoObj = {text:newTodo,id:Date.now()}
    toDos.push(newTodoObj)
    paintTodo(newTodoObj)
    saveTodos()
}

toDoForm.addEventListener("submit",handleTodoSubmit)

 todolist사이트의 꽃인 todolist를 만들어주는 구문이다

localstorage를 사용해서 새로고침을 해도 작성했던 todolist가 사라지지 않는다.

작동을 설명해 주자면 todolist를 작성하는 input에 할 일을 작성하고 엔터를 누르면

submit이벤트가 발생하게 되고 submit 이벤트가 발생하면 handleTodoSubmit함수가 실행된다. 

 

handleTodoSubmit

function handleTodoSubmit(e){
    e.preventDefault() //새로고침되는 이벤트를 막는다
    const newTodo = toDoInput.value //인풋태그에 입력된값을 newToDo라는 변수에저장
    toDoInput.value = "" //인풋태그를 비워줌
    //입력받은값을 객체로만든다 text키에 입력받은값,id키에 현재시간(밀리초단위)
    const newTodoObj = {text:newTodo,id:Date.now()} //id는 나중에 삭제할때 값을 구분하기위해사용
    toDos.push(newTodoObj) //객체를 toDos배열에 넣어준다.
    paintTodo(newTodoObj) //객체를 넘겨받아서 태그를 만들고 태그에값을넣은다음 추가해주는함수
    saveTodos() //toDos배열을 localStorage에 저장해주는 함수.
}

paintTodo

function paintTodo(newTodoObj){
    const li = document.createElement('li') //li태그생성
    li.id = newTodoObj.id //li태그에 id를 추가 ,,객체의id 현재시간(밀리초)
    const span = document.createElement('span') //span태그 생성
    const button = document.createElement('button')//button태그 생성
    button.innerText = "❌" // == <button>❌</button>
    button.addEventListener('click',deleteToDo) //button에 클릭이벤트 생성
    li.appendChild(span); // == <li><span></span></li>
    li.appendChild(button); // == <li><span></span><button></button></li>
    span.innerText = newTodoObj.text; //<span>{newTodoObj.text}<span> 작성한 할일이 들어감
    toDoList.appendChild(li) //li를 todolist가 있는 ul에 붙여줌
}

saveTodos

function saveTodos(){
    localStorage.setItem(UserId,JSON.stringify(toDos))
    //localStorage.setItem(저장할키,저장할내용)
}

userId를 키값으로 저장해서 같은 유저아이디로 들어와야 같은 값을 저장할 수 있고 읽을 수 있다.

다른 키로 들어오면 다른 키에 저장된다.

JSON.stringify()는 값이나 객체를 JSON문자열로 변환시켜 준다.

(localStorage에는 문자열만 저장되기 때문에 문자열로 바꿔야 한다.)

 

이제 값을 저장했으니 새로고침 했을 때 값을 불러와서 다시 paintTodo로 그려주면 된다.

 

이 구문이 아까 login에 있던 구문이다.

if(UserId){ //로그인한값이있는지 검사
        savedToDos = localStorage.getItem(UserId); //로그인한 아이디로 localstorage에서 값을 받아옴
        if(savedToDos){ //입력한아이디로 localstorage에서 받아온값이 있으면 실행
            const parsedTodos = JSON.parse(savedToDos) //문자열로만든 값을 다시 배열로바꿔준다.
            toDos = parsedTodos //toDos변수에 받아온값을 넣어준다 넣어주지않으면 
            //새로 todolist를 입력했을때 toDos값이 비어있기때문에 localStorage값이 새로 입력된다.
            parsedTodos.forEach(newTodo => { //받아온값을 forEach로 paintTodo함수를 다시돌려준다.
                paintTodo(newTodo)
            });
        }
    }

삭제기능

deleteToDo

function deleteToDo(e){
    const li = e.target.parentElement //클릭한버튼의 부모요소인li를 받아옴
    li.remove() //받아온부모요소인li를 지워줌
    toDos = toDos.filter(toDo => toDo.id !== Number(li.id)); 
    //toDos에서 filter를 돌려서 클릭한li요소와 같지않은값들을 리턴. 해서 toDos에 넣어줌
    saveTodos() //localStorage업데이트
}

 

값이 변경될 때마다 toDos배열을 변경시키고 변경된 toDos값을 localStorage에 저장해 주는 방식이다.

 

 

backgroundimg.js

const background = document.querySelector("#background")
const imgs = ['bg1','bg2','bg3','bg4','bg5','bg6','bg7','bg8']
const random = Math.floor(Math.random()*imgs.length)
background.style.background = `url(./imgs/${imgs[random]}.jpg)`
background.style.backgroundSize =  'cover'

 이미지의 이름을 배열로 만들고 이미지배열길이범위만큼 랜덤한숫자를 만들어서 랜덤 하게 이미지를 받아올수있게 해서 넣어줬다. 새로고침될때마다 랜덤숫자가 바뀌게되고 랜덤하게 배경이미지를 받아올 수 있다.

 

advice.js

fetch("https://api.adviceslip.com/advice").then(res=>res.json()).then(
    data => document.querySelector('#quote span:last-child').innerText=(data.slip.advice)
)

명언을 랜덤 하게 보여주는 구문이다 . 원래 수업에서는 배열에 명언을 넣어놓고 랜덤하게 하나씩받아와서 html에 넣어주는 거였는데 명언을 구할시간도 없고 이쪽이더 공부하기 좋아보여서 그냥 랜덤하게 명언을 보여주는 api에 요청해서 값을 받아서 넣어주었다.

 

aaa로 들어가서 todo를 작성해 줬다.

 

bbb로 로그인해서 들어가면

아무런 값도 뜨지 않는다.

 

로컬스토리지를 들어가서 보면 각각 다른 키에 저장되어 있는 걸 볼 수 있다.

 

로컬스토리지는 f12키나 개발자도구에 들어가서 애플리케이션에 들어가면 볼 수 있다.

 

내용자체가 되게 어렵지는 않았지만 내가 따로 더 추가해서 로그인한 사람에 따라 다르게 보이는 기능을 추가해 줬다. 제대로 로그인기능을 만들어서 해도 좋을 것 같다. 혹시 설명에 이해가 안 가는 부분이 있다면 댓글로 알려주시면 확인해 보겠습니다. 

 

반응형
반응형

리액트 라우터 나 라이브러리를 사용하지 않고 순수 자바스크립트만으로 라우터를 구현해서 Single Page Application을 구현하는 문제를 학원에서 풀어보았다. 이런 걸 해볼 때마다 라이브러리 만든 사람방향으로 절을 올리고 문제를 만든 사람 방향으로는 두 번 절을 올리고 싶은 심정이다. 한번 풀어보쟈!

 

코드샌드박스로 완성본을 올려놓고싶었는데 같은 코드가 코드샌드박스에선 실행이 안된다.. 어째서...ㅜㅜ

그래서 캡쳐로 대신하겠다.

 

index.js

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <nav class="navbar">
        <a href="/">HOME</a>
        <a href="/post/123">POST</a>
        <a href="/shop">SHOP</a>
    </nav>
    <div id="app"></div>
    <script src="./main.js" type="module"></script>
</body>
</html>

각 페이지로 넘어갈 버튼들의 마크업이다 자바스크립트를 연결해 주는데 데브매칭 문제들은 대부분 컴포넌트로 구현하기 때문에 type을 module로 하고 나머지 요소들을 자바스크립트에서 컴포넌트로 만들어서 넣어준다.

 

style.css

#app{
    text-align: center;
    color: #2c3e50;
    margin-top:60;
}

.navbar{
    margin-top:60px;
    text-align: center;
}

.navbar > a{
    display: inline-block;
    font-size: 32px;
    text-decoration: none;
    border-radius: 18px;
    background-color: #8040ff;
    color: white;
    padding: 5px 10px;
} 
.navbar > a:hover{
    transform: scale(0.95);
}

// 라우팅되었을때 렌더링될 컴포넌트의 스타일
.mainPage{
    background-color: #4a4b43;
    padding:50px 0;
    color:white
}
.postPage{
    background-color: #40d2ff;
    padding:50px 0;
    color:white
}
.shopPage{
    background-color: #ff40cf;
    padding:50px 0;
    color:white
}

css는 딱히 설명할 게 없다.. 아직 화면에 렌더링 되지 않은 컴포넌트의 스타일도 미리정의해 놨다는 거 정도?

 

html,css 까지 끝난상태

여기까지 하면 이렇게 예쁜 버튼 3개(버튼태그는 아니지만)가 만들어진 걸 볼 수 있다.

 

이제 자바스크립트로 들어가 보자.

우선 컴포넌트들과 필요한 모듈들을 만들 src폴더를 생성해 준다 모든 모듈은 src폴더 내에서 관리해 준다.

 

src / app.js

// import Router from "./router.js"
// import { navigate } from "./utils/navigate.js"

export default function App({target}){
    this.container = target
    const init=()=>{
        document.querySelector(".navbar").addEventListener('click',(e)=>{
            e.preventDefault()
            const targetURL = e.target.href.replace("http://127.0.0.1:5500","")
            console.log(targetURL);
            // navigate(targetURL)
        })
        // new Router(target)
    }
    init()
}

주석처리 한 부분은 아직 구현하지 않은 부분이다 이따가 차례대로 구현해서 넣어주자.

App컴포넌트는 target을 인수로 받는다 target 은 App컴포넌트에서 렌더링 해줄 요소들이 들어갈 위치이다.

target = document.querySelector("#app")

init메서드를 만들어준다

init메서드는. navbar

    <nav class="navbar">
        <a href="/">HOME</a>
        <a href="/post/123">POST</a>
        <a href="/shop">SHOP</a>
    </nav>

이 부분에 이벤트를 클릭이벤트를 생성하고 클릭했을 때 a태그의 페이지가 이동되는 이벤트를 막아주고 preventDefault()

클릭한 a태그의 href 주소를 받아서 앞의 로컬주소는 공백으로 처리해서  a태그에 달아놓은 주소만 받는다.

"http://127.0.0.1:5500/shop"  ==> "/shop"으로 변환시켜 준다.

init() 만들어놓은 메서드를 실행. App컴포넌트가 불려서 실행되면 실행된다.

 

main.js

import App from "./src/app.js";

window.addEventListener("DOMContentLoaded",()=>{
    new App({target:document.querySelector("#app")})
})

만들어놓은 App모듈을 연결시켜 준다 html과 연결된 유일한 자바스크립트는 main.js하나이고 

main.js에서 App모듈을 불러서 렌더링 해주고 App모듈에 여러 모듈들을 불러서 렌더링 해준다

 

index.html -- main.js -- app.js -- 모듈 1

                                                --모듈 2

                                                --모듈 3

 

이런 식으로 연결해서 사용해 준다 , 리액트와 비슷한 것 같기도 아닌 것 같기도..

 

이제 app에서 필요한 요소들을 하나씩 만들어주자.

a태그를 클릭했을 때. 이벤트를 막고 href를 받았다. 이 href를 받아서 넘겨주고 주소창에 추가해줘야 한다.

 

href를 받을 커스텀이벤트 함수를 만들어주자

src / utils / navigate.js

export const navigate = (to)=>{
// const 이벤트개체 = new CustomEvent("이벤트이름",{detail:{키:밸류}})
    const historyChangeEvent = new CustomEvent("historyChange",{
        // to 이동하게 될 url
        detail:{to}
    })
    dispatchEvent(historyChangeEvent);
}

 나도 커스텀이벤트함수는 처음 본 거라 좀 생소한데 navigate함수를 실행하면 to라는 파라미터를 받고 

CustomEvent를 만들 때 detail에 to키에 넣어준다. 

만들어진 이벤트개체를 dispatchEvent 해주면 이벤트를 호출한다. 

 

navigate함수를 실행하면 이벤트함수를 만들고 이벤트함수를 호출한다. 

여기서는 호출되었을 때의 동작은 없기때문에 이따가 다른 모듈에서 호출되었을때의 동작을 만들어 준다.

 

렌더링 할 페이지들을 먼저 만들어주겠다.

src / pages / Main.js

export default function Main(target){
    console.log(target)
    this.container = target
    this.setState = ()=>{
        this.render();
    }
    this.render = () =>{
        this.container.innerHTML = `
        <div class = "mainPage">
        메인 페이지에요.
        </div>
        `;
    }
    this.render()
}

src / pages / Pages.js

export default function Shop(target){
    this.container = target
    this.setState = ()=>{
        this.render();
    }
    this.render = () =>{
        this.container.innerHTML = `
        <div class = "shopPage">
        SHOP페이지에요.
        </div>
        `;
    }
    this.render()
}

src / pages / Post.js

export default function Post(target){
    this.container = target
    this.setState = ()=>{
        this.render();
    }
    this.render = () =>{
        this.container.innerHTML = `
        <div class = "postPage">
        POST페이지에요.
        </div>
        `;
    }
    this.render()
}

target을 전달받아서 target에 html요소를 렌더링 해주는 모듈들이다. this.setState메서드는 지금은 없어도 상관없긴 한데 나중에 값을 전달받거나 할 때 사용할 수도 있어서 만들어두었다.

 

이제 app에서 href를 받아서 유효성을 검사하고 해당하는 href에 page를 보낼 수 있는 모듈을 만들어보자

src / constant / routerinfo.js

import Main from "../pages/Main.js";
import Shop from "../pages/Pages.js";
import Post from "../pages/Post.js";

export const routes = [
    {path:/^\/$/, element:Main}, // "/"
    {path:/^\/post\/[\w]+$/, element:Post},  // "/post/123 , 456"
    {path:/^\/shop$/, element:Shop}, // " /shop "
]

배열의 path에는 정규표현식이 작성되어 있어서 path로 유효성을 검사하고 알맞다면 해당 객체를 받을 수 있는 배열메서드를 사용해서 element에 있는 모듈을 받아올 수 있다.

 

이제 모듈을 받아서 렌더링 해주고 href주소를 받아서 주소창에 입력해 줄 모듈 router를 만들어보자.

src / router

import { routes } from "./constants/routerinfo.js";

export default function Router(target){
    this.container = target;
    const findMatchedRoute = () =>{
        //pathname 이 정규표현식에 true로 나오는 객체를리턴
        return routes.find(route => route.path.test(location.pathname)); //test 메서드는 정규표현식에 값을넣어서 true false 로 반환함
    }
    
    const route = () =>{
        const TargetPage = findMatchedRoute().element;
        new TargetPage(target)
    }
    const init = () =>{
        window.addEventListener("historyChange",({detail})=>{ //커스텀이벤트에서 전달해주는 detail을 객체구조분해할당으로 받음
            const {to} = detail;
            history.pushState(null,"",to); //페이지이동없이 주소를바꿔주는 메소드 (넘겨줄데이터,타이틀,변경할주소)
            route() //pushState로 주소를 변경해주고 라우트함수를 실행시킴
        })
        window.addEventListener("popstate",()=>{
        //뒤로가기버튼을 클릭했을때 실행되는 이벤트
        //주소가 이전주소로바뀌면서 route를 실행하면 이전주소에 해당하는 컴포넌트렌더링
            route()
        })
    }
    init();
    route();
}

router는 target을 전달받아서 렌더링 할 요소에 전달해 준다.

init( ) 메서드는 아까 dispatch 한 커스텀이벤트의 동작을 만들어준다 historyChange이벤트가 발생했을 때 이벤트에서 전달해 주는 detail을 파라미터로 함수 안에서 사용하고 detail안에 있는 to를 구조분해 할당해서 사용한다

to  에는 "/" , "/shop" , "/post/123" 같은 클릭한 a태그의 href 값이 들어있다.

history.pushState는 주소창을 변경시켜 줄 수 있는 메서드이다 pushState(넘겨주는 데이터, 타이틀, 변경할 주소)가 파라미터로 들어간다 여기에 to를 넣으면 현재주소에서 /shop 이 추가된 주소로 바뀐다 (페이지변경 없이!!!)

주소창의 주소를 변경시켜 주고

route메서드를 실행시키는데 

route메서드는 findMatchedRoute메서드를 실행시키고 값을 리턴 받은 후 리턴 받은 값의 element요소를 TargetPage에 할당한다

findMatchedRoute는 아까 만든 정규표현식 배열을 이용하는 메서드인데 정규표현식배열에 find메서드를 이용해서 true값을 리턴하는 배열요소를 받아온다. 현재주소(location.pathname)를 배열에

정규표현식이 들어있는 path키에 test메서드(정규표현식을 만족하는지 판별하는 메서드 맞으면  true 아니면 false 리턴)

안에 넣어서 정규표현식을 만족하는지 검사한 후 리턴해준다.

 

 

이제 99.99% 완성이다 아까 App에서 주석으로 처리해 놨던 구문들을 풀어주면 끝난다

src /app.js

import Router from "./router.js"
import { navigate } from "./utils/navigate.js"

export default function App({target}){
    this.container = target
    const init=()=>{
        document.querySelector(".navbar").addEventListener('click',(e)=>{
            e.preventDefault()
            const targetURL = e.target.href.replace("http://127.0.0.1:5500","")
            console.log(targetURL);
            navigate(targetURL)
        })
        new Router(target)
    }
    init()
}

 

 init 메서드를 실행하면 

a태그의 이벤트를 막고 navigate에 targetURL을 넣어서 historyChange이벤트를 만들고 디스패치해 주고

Router에 target을 넣어서 실행시켜 준다 

그러면 Router에서 클릭한 주소를 historyChange이벤트에서 받아서 historypush.state로 주소에 넣어주고

해당하는 주소를 배열에서 찾아서 렌더링 할 모듈을 받아와서 타깃위치에 렌더링 해준다.

 

완성이미지

012
각 모듈이 렌더링될때의 이미지

 

 

나도 처음 배운 내용이고 이해하기 쉽게 좀 길게 풀어서 설명하다 보니 두서없는 설명이 된 것 같은데 일단 나는 알아볼 수 있으니 복습용으로는 충분하지만 혹시라도 누군가가 보고 이해가 안 가는 게 있다면 댓글로 알려주면 답 해줄 수 있을 것 같다 

아니면 내 깃허브를 보거나..

 

GitHub - cokeholic-kim/spaVanillaJS

Contribute to cokeholic-kim/spaVanillaJS development by creating an account on GitHub.

github.com

 

반응형
반응형
interface Dog{
	readonly name:string
    age:number
    color?:string
}

let dog1:Dog ={
	name:"구름",
    age:3
    //color 가 없어도 에러나지않음 
}
let dog2:Dog = {
	name:"몽이",
    age:2,
    color:"black"
}

 

optional 

? 키워드로 사용할수있다 optional로 만들어진 타입은 말그대로 옵션이기때문에 선언할때 있을수도있고 없을수도있다 포함하지 않는다고해서 에러가 나지않는다.

 

interface

객체의 타입을 선언할때 사용하는 키워드이다. 타입을 확장할때에 type으로 선언한것보다 편리하고 성능적으로 객체의 타입을 만들때에는 interface를 사용하는걸 권장한다.

interface Dog{
	name:string
    age:number
}
//확장

interface Dog{
	color:string
}

type Dog = {
	name:string
    age:number
}
//확장
type Dogstype = Dog&{
	color:string
}

interface는 같은 타입이름을 사용해서 선언하는것으로 확장이 가능하다.

interface는 객체의 타입을 선언하는것만 가능하다.

interface에 메서드의 타입을 만들어서 받는것도 가능하다

interface TV{
	turnon():boolean; //파라미터로 아무것도 받지않고 논리값을 리턴
    turnoff():void; //파라미터로 아무것도받지않고 아무것도 리턴하지않음
}​

const myTv:TV = {
	turnOn(){
    	return true
    },
    turnOff(){
    	console.log("off")
    }
}

 

readonly

읽기전용으로 만들고 객체와 배열에서만 사용가능하다.

interface Dog{
	readonly name:string
    age:number
    color?:string
}

let dog1:Dog ={
	name:"구름",
    age:3
    //color 가 없어도 에러나지않음 
}

dog1.name = "초롱이"  //error readonly는 수정할수없다.

읽기전용으로 타입을 선언하면 객체의 요소를 읽을수는 있지만 수정할수는 없게된다.

 

 

 

반응형
반응형

타입스크립트 기본타입사용법

 

1)타입추론

 타입스크립트는 타입 표기가 없는 경우 코드를 읽고 분석하여 타입을 유추할수있음

let a = "abcde"  //string 타입으로 지정됨

2)타입 명시

  변수 선언시 변수값의 타입을 명시함으로써 변수값의 데이터 타입을 지정

let a:string = "abcde" //string타입으로 지정된변수에 스트링값 대입

3)타입선언

1.변수 타입 선언

type 변수이름 = 타입

number,string,boolean,null,undefined,object,array

항목의 값을 지정할수없을때는 any 타입을 지정

type APPLE = string //타입을 변수로 만들고
let apple:APPLE = "string" // 선언하는 변수에 타입변수를 넣어서 사용

2.유니언타입(다중타입)

let array:(string | number)[] //string 배열 이나 number배열이 들어갈수있는타입
let strnum:(string | number) //string이나 number가 들어갈수있는 타입

지정된 타입 이외의 값을 넣으면 에러가 발생하고 자바스크립트로 컴파일되지않는다.

 

함수타입선언

 

1.인수와 반환값이 있을때

let myFunction : (arg1:number , arg2:number)=>number //파라미터로 넘버타입이들어가고 리턴값이 넘버
myFunction=function(x,y){
	return x+y
}
//함수의 타입을 먼저 만들어놓고 함수를 만드는 방식

function myFunction(a:number,b:number):number{
	return x+y
}
//위와 타입은 같지만 함수를 만들때 타입을 한번에 주는방식

 

2.인수의 반환값이 없을때 void 키워드 --> 리턴없음

let myFunction:()=>void;
myFunction = function(){
	console.log('hi')
}
//변수에 타입을 먼저 정한후에 함수를 만들어주는 방법
function myFunction():void{
	console.log("hi")
}
//파라미터도없고 리턴도없는경우
반응형
반응형

타입스크립트 설치

타입스크립트를 설치하기 위해서는 Node.js를 설치해야한다.

https://nodejs.org/ko/

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

노드를 설치해주고

터미널을 실행시킨 다음 아래코드를 입력해주면 타입스크립트가 설치된다.

npm install -g typescript

 

타입스크립트를 설치하고나면 

이제 프로젝트를 만들 폴더에서 .ts 확장자로 파일을 만들어서 타입스크립트 코딩을 시작하면된다.

 

타입스크립트 컴파일설정

타입스크립트 프로젝트 폴더안에

tsconfig.json파일을만들어서 컴파일할때의 설정을 만들어줘야한다. 

 

 

Typescript 컴파일시 세부설정 (tsconfig.json) - 코딩애플 온라인 강좌

tsconfig 파일 생성하기 여러분 프로젝트 폴더에  tsconfig.json 이라는 파일을 하나 생성합시다. 여기엔 타입스크립트 ts 파일들을 .js 파일로 변환할 때 어떻게 변환할 것인지 세부설정이 가능합니다

codingapple.com

이렇게 json형식으로 옵션을 설정해주면된다.

 

타입스크립트로코드를 작성한후 터미널을켜서 tsc 를 입력하면 자바스크립트로 컴파일된다.

**혹시 powershell 에서  tsc사용이 안된다면 cmd로 바꿔서 실행하면 된다 powershell 에서는 정책문제때문에 tsc를 사용하지 못할수도있다.

 

이렇게 컴파일까지 할수있으면 타입스크립트를 사용할 준비는 끝이 났다

이제 타입스크립트를 사용하러 가보자. 

 

 

 

 

반응형
반응형

타입스크립트

 

http://www.typescriptlang.org

 

JavaScript With Syntax For Types.

TypeScript extends JavaScript by adding types to the language. TypeScript speeds up your development experience by catching errors and providing fixes before you even run your code.

www.typescriptlang.org

 

2012년에 발표됨 

대규모 javascript 애플리케이션 개발용도

마이크로소프트 개발, 오픈소스 프로그래밍 언어

자바스크립트의 상위집합이다 (자바스크립트의 모든기능을 포함하고 자바스크립트에 없는 기능도 가지고있음)

 

*자바스크립트와의 가장 큰 차이점!

자바스크립트는 동적인 언어이며 프로그램 구동중에 타입이 다이내믹하게 변경되지만

타입스크립트는 정적인 언어이며 프로그램을 작성할때. 타입을 정의해준다.

 

타입스크립트의 장점

1.버그를 줄이고 유지보수하기 쉽고 질좋은 코드 작성에 용이하다.

2.강력한 타입으로 대규모 개발에 용이하다.

3.자바스크립트 라이브러리와 편리한 사용

4.개발도구에서의 강력한지원.

 

ex) 자바스크립트를 실행하기전에는 알수없던 에러를 타입스크립트는 코딩하는과정중에 알수있게 도와준다.

 

프론트엔드 프레임워크와 타입스크립트

리액트(React)

         리액트와 타입스크립트의 호환성은 좋은편이다

         간단한 옵션을 추가하는것으로 타입스크립트를 사용할수있도록 지원한다.

뷰(vue)

         뷰 2.0부터 타입스크립트 사용가능

         뷰 3.0부터 타입스크립트 공식 지원

앵귤러(angular)

        버전 2부터 타입스크립트를 기반으로 만들어졌고 타입스크립트를 권장하고 있다.

 

 

타입스크립트를 사용할때에는 코드에디터로 비쥬얼스튜디오 사용을 권장하는데 비쥬얼스튜디오도 마이크로소프트에서 만들었고 타입스크립트도 마이크로소프트에서 만들어졌기때문에 자동완성이나 코드를 입력하는부분에서 더 부드럽게 작동한다고 한다.

 

반응형
반응형

마지막으로 글을쓴게 2월2일인데 마무리하고 정리하고 올리려니까 벌써 21일이 되버렸다.. 

원래는 중간중간 진행상황같은걸 정리해서 올려보고싶었는데 프로젝트를 진행하는데만 힘을너무쏟아서 블로그쪽은 생각도 안하고있었다.. 멀티가 이렇게나 안되다니.. 

프로젝트 끝난것도 저번주 월요일인데 프로젝트 끝나고 일요일에 정처기 필기시험이있어서 공부하느라 또 정리를 못하고 결국 22일이 되어버렸다.

 

그래도 어떤방법으로 만들었고 어떤걸 만들고싶었었는지 다시한번 정리해놔야 나중에 어떻게만들었는지

기억이 날것같아서 정리를 해볼려고한다.

 

 

프로젝트이름

The나들이

(떠나자 나들이 라는 의미로 만들었다)

 

기능

설정되있는 여행지 중에서 사용자가 선택하고 추천해주는 여행지중에서 여행 경로를 확인하고 관련한  정보를 얻을수있는 사이트

 

사용한라이브러리

@react-google-maps/api   구글지도기능을 이용하기위해 설치

redux-devtools/extension   redux상태를 파악할수있는 미들웨어 

html2/canvas                     화면을 캡쳐할수있게 해주는 라이브러리

antd                                    만들어진 스타일을 이용할수있는 라이브러리

node-sass                          scss를 이용해서 스타일을 적용하기위해 사용

axios                                   get,post요청을 하기위해서 사용.

react-cookie                        로그인 정보를 쿠키로 저장하기위해서 사용.

react-datepicker                  캘린더를 만들어주는 라이브러리

react-router-dom                라우터로 컴포넌트를 마운트시키는 라이브러리

react-redux                          리덕스로 상태값을 관리하는 라이브러리

react-icons                           아이콘을 만드는 라이브러리

styled-components             스타일이 적용된 컴포넌트를 만들어주는 라이브러리

 

필요할때마다 하나씩 설치를 하다보니 꽤 많은 라이브러리를 사용하게 됐다.. 점점 용량이 커지고 npm install 할때 걸리는 시간이 늘어나는게 이게 성능적으로 문제가 없나 싶었다.

꼭 필요한 라이브러리만 설치해서 사용하는게 좋은것같다.

(scss랑 styled-components 같이쓴건 좀 오바였지 싶다..)

 

데이터베이스

City 테이블 :각 나라별 위치좌표,이름,구글맵스에서 사용할 확대값 ,관련 블로그포스팅주소 , 월별 여행관련정보

SpotPlace 테이블: 나라의 추천관광지 이름,좌표,추천관광시간,이미지주소,추천경로순서

member 테이블: 회원가입관련자료: 유저코드,닉네임,비밀번호(암호화),이름,이메일

 

구현된 화면

팀원이 4명이고 다들 해외에서 살다오거나 연수경험이 있어서 각자 자기 지역을 담당해서

4개의 추천여행지를 만들었다

(캐나다,하얼빈,싱가포르:이건내가함!,제주도)

 

1200px 과 768px 에 반응형 디자인을 만들었다.

 

이제 실질적인 기능들이 모두 들어있는 일정만들기 페이지로 들어가보면.

기능이 좀 많은데 현재시간으로 제주도 날씨를 알려주고 (wheatherOnAPI를 이용했다)

달력으로 여행을할 기간을 찍으면 그 기간에 해당하는 여행팁을 알려준다.

오른쪽이 우리가 만들어놓은 추천 여행지인데 +를 눌러서 하나씩 일정을 추가시키면

오른쪽에서는 빠지고 왼쪽에 장소 추가되고 지도위에 마커가뜨고 마커들끼리 선으로 이어져서 동선을 쉽게 확인할수있다. (googlemapsAPI Marker , polyline 이용)

 

오른쪽 추천장소에 마우스를 올리면 해당장소로 지도의 포커스가 옮겨진다

(googlemaps 의 panTo기능을 이용)

아래 여행팁에 가려져있는 부분에 가고싶은 장소를 검색하면 해당장소로 포커스가 옮겨진다.

(구글지도의 자동완성기능사용 autocomplete)

 

지도위의 옵션버튼

 

여행기를 클릭하면 여행기블로그 포스팅이 연결된 팝업을 띄워준다.

<iframe> 태그를 이용해서 블로그 포스팅이있는 주소를 연결해서 보여줌

 

추천일정을 클릭하면 우리가 설정한 추천여행지와 추천코스 순서대로 보여준다

 

추천장소의 이미지가 슬라이더로 자동으로 넘어가고 아래 태그로 추천일정을 하나씩 선택하거나 모두선택버튼으로 모두선택할수있다. 선택하면 왼쪽에 장소가추가되고 지도에 마커와 경로가 그려진다.

장소들의 순서는 가장 좋다고생각하는 동선으로 순서를 정해서 하루정도에 돌아볼수있는 코스로 짜봤다.

 

 이제 일정을 모두 정하고 나면 일정생성버튼을 클릭하면 만든 경로의 이미지를 캡쳐해서 저장해준다.

 

기능들을 만들면서 좀 힘들었던 점이 많았는데 구글맵스 API가 자바스크립트로 사용하는 예시들은 인터넷에 많이 있는데 리액트라이브러리로 만드는 방법들은 예시가 많이 없어서 공식문서를 보고 여러가지 방법을 시도해서 만들었다. 사용된 예시가 많이 없어서 참고할만한게 별로없어서 힘들었다.

 

그리고 이미지를 주소에서 받아오다보니 이미지를 제대로 못받아와서 엑스박스가 뜨는경우가 있었다. 이런 문제를 처음봐서 당황했는데 이미지 태그에 옵션을 추가해주는걸로 해결할수있었다.

<img src={img} referrerpolicy="no-referrer" />

 

 

이미지를 캡쳐하는 라이브러리인  html2canvas도 처음사용해보는거라 어려웠는데 구글mapsAPI를 사용하니까  계속  CORS에러가 나서 이것도 열심히 구글링해서 구문한줄 추가해주니까 해결되었다.

html2canvas(document.getElementsByClassName('App')[0],{ useCORS: true }).then(canvas=>{
           onSaveAs(canvas.toDataURL('image/png'),'image-download.png')
})

 

 

 

페이지가많거나 내용이 많은 페이지는 아니었지만 기능적으로 새로운 api를 사용해보고 새로운걸 알아가면서 해야해서 시간이 좀 많이 걸렸던것같다.

API 관련한 부분은 선생님께 물어볼수도없어서 팀원들끼리 정말 열심히 찾아가면서 했던것같다. redux도 처음에 만들었던 reducer에서  2번정도는 구조를 바꾼것같다.

만들다보니 계속 막히는 부분이 생겨서 처음부터 제대로 다 구상을 해놓고 시작하면 이렇게 안해도될텐데 라는 생각을 계속했던것같다.. 사실 다 구상해놓고 만들수도 없지만..ㅠ 

 

4명이서 만들다보니 깃허브로 처음협업을해봤는데 종종 충돌이 생기고 머지가 안되는 상황이 종종 있었는데. 

이것도 찾아보면서 해결하다보니 확실히 팀으로 해보길 잘했다는 생각이 들었고 깃허브가 진짜 협업에 좋고 정신에 조금 해롭다는 걸 배웠다..

 

이제 개인프로젝트를 해야되는데 이번경험으로 리액트로 프로젝트만드는법에 조금 익숙해진것같아서 개인프로젝트는 조금 더 쉽게 할수있을것같은 생각이든다.

반응형

+ Recent posts