반응형

이번에 리액트로 쇼핑몰 프로젝트를 연습삼아 만들어보게되면서 닷홈의 무료 호스팅을 신청해서 

그 서버에 빌드한 파일을 올리는 방법으로 호스팅해주었다. 과정은 간단한데 로컬에서 파일을 빌드하고 닷홈서버의 html폴더에 빌드한 파일을 넣어주기만 하면되는거라서 간단하게 자동화가 가능할것같았다. 그래서 github action을 이용해서 업데이트 자동화를 해보기로 했다. 

 

우선 깃허브에 코드를 올려주고 해당 리포지토리로 이동해준다.

리포지토리에서 Actions로가서 simpleworkflow를 검색한다음 configure를 클릭한다.

그러면  생성한 리포지토리의 .github/workflows 경로에 blank.yml 파일을 생성해주게 된다.

이제 이파일에 필요한 동작들을 작성해주면 코드가 푸쉬됐을때 깃허브에서 띄워주는 가상의 컴퓨터에서 입력한 동작들을 수행해준다 

 

내가작성한 코드를 올려줄테니 참고해서 닷홈에 자동화로 올리는 걸 많이 시도해봤으면 좋겠다.

name: CI/CD Pipeline

on:
  push:
    branches:
      - main # 메인 브랜치가 푸쉬될때 동작

jobs:
  build:
    runs-on: ubuntu-latest # 사용할 os 버전 

    steps:
    - name: Check out code
      uses: actions/checkout@v2

    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '18' # 사용할 Node.js 버전을 설정합니다.

    - name: Install dependencies
      run: npm install # 프로젝트의 의존성 설치 

    - name: Build project
      run: npx vite build # 프로젝트 빌드 명령어를 실행합니다.

    - name: List dist directory contents
      run: ls -al dist # 빌드된 파일의 폴더로 이동 
      
    - name: Install LFTP
      run: |
        sudo apt-get update
        sudo apt-get install -y lftp # ftp 프로그램 설치 

    - name: Transfer files via SCP
      env:
        SERVER_HOST: ${{ secrets.SERVER_HOST }}
        SERVER_USERNAME: ${{ secrets.SERVER_USERNAME }}
        SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
      run: 
        lftp -u  ${{ secrets.SERVER_USERNAME }},${{ secrets.SERVER_PASSWORD }} ftp://${{ secrets.SERVER_HOST }}:21 -e "
        mirror -R dist /html;
        bye
        "

 

이렇게 설정하면 원격컴퓨터에서 노드를 설치하고 파일의 의존성을 설치한다음 빌드 하고 해당 파일을 원격지의 닷홈서버로 ftp프로토콜로 전송하게되는 코드이다. 

vite를 사용해서 dist 폴더에 저장되는것같은데 build명령어나 의존성을 설치하는명령어는 사용하는 패키지 매니저나 명령어에 따라서 다르기때문에 자신의 환경에 맞춰서 써주면 될것같다. yarn을 이용하는사람은 yarn 명령어를 사용해야할테고.. 

 

닷홈은 ssh 를 열어놓지않기때문에 수작업으로 할때는 빌드한 파일을 파일질라로 닷홈서버에 옮겼는데 이렇게하면 메인 브랜치에 코드가 푸쉬될때 자동으로 빌드한파일을 닷홈의 /html폴더로 이동시켜서 코드만올려도 자동으로 업데이트 되는 효과를 볼수있다. 프로젝트가 복잡해지면 다른 추가적인 과정도 필요하게 될수도있지만 현재로서는 잘 동작하는듯하다.

반응형
반응형

프로젝트를 만들려고 다시 옛날에 만들었던 코드를 보니 디자인이나 구성요소를 가져가기에는 
맘에 들지않는 부분이많아서 디자인이나 기능 부분도 새로 만들어보기로 했다. 

 

참고로 나는 디자인에 소질이 정말 없다고 자부하는데 고르는 색깔마다 별로 좋은 반응을 받은적이없어서 AI툴을 이용해보기로 했다 

 

https://v0.dev/

 

v0 by Vercel

Chat with v0. Generate UI with simple text prompts. Copy, paste, ship.

v0.dev

next.js 를 만들고 vercel 을 만드는 곳에서 개발한 AI툴인데 프롬프트에 필요한 디자인을 입력하면 
해당 컴포넌트와 필요한부분들이 모두 코딩되어서 코드수준으로 만들어준다. 

우선 내가 필요한 페이지를 정리해 보았다. 

가장 먼저 보여지는 index 페이지는 베스트 셀러나 추천하는 상품과 배너가들어갈수있는 페이지로 구성하고 

각각 페이지들은 상품의 카테고리로 들어갈수있는 전형적인 쇼핑몰 형태의 페이지로 구성한다

추가적으로 관리자 페이지가 필요한데 관리자의 index페이지는 세일즈코스트나 잘 팔리는 상품들을 그래프로 조회할수있는 패널형태의 페이지로 구성하고 각각 페이지에 관리자에게 필요한 기능을 넣은 페이지로구현하려고 한다.

 

관리자에게 필요한기능은 구현하면서 계속 추가될것같지만 우선적으로 필요한기능은 

1. 매출액 조회 (월단위 , 일단위 , 지정한 시작 종료일 기준)

2. 상품등록 , 삭제 , 수정 

3. 회원관리

4. 재고관리 

5. 할인또는 프로모션의 관리 

6. 공지 기능 

벌써 꽤 머리가 아플것같은 기능들이있는데 아마 진행하면서 더 늘어날수도있을것같다. 한 번에 다 구현은 할수없으니 하나씩 천천히 구현 해봐야할것같다. 현재로써는 상품에 댓글 기능은 포함하지않는 기획이라 댓글이나 답글은 없어도될듯하다. 사실 최초 기획단계에서 필요한 요소를 모두 정의하고 들어가면 좋겠지만 코딩을 공부하는 입장에서 그렇게까지하면 코드를 공부하는 시간보다 기획에 시간을 너무 쏟게 될것같아서 기능이 추가될수있다는 점만 염두에두고 확장성을 고려하여 코드작업을 해보도록 하겠다. 그쪽이 더 공부가 될것같고... 이전 기획과 새로운 기획에서 충돌이 났을떄의 해결도 좋은 경험이 될것같다.

 

나는 꼭 vite + typesciript + react  연습을하기 위해서 v0.dev로 만들고 react 만 사용하는 환경으로 옮기느라 고생을좀했지만 next.js프레임워크를 사용한다면 v0.dev가 만든 코드를 그대로 프로젝트로 생성해서 사용할수있어서 더 편하게 이용할수있다 .

 

해당 페이지는 빌드해서 호스팅사이트에 올려놓았다. 무료 호스팅이라 비용없이 간단하게 이용할수있어서 좋은것같다

corona456.dothome.co.kr

 

Vite + React + TS

 

corona456.dothome.co.kr

 

v0.dev 로만든 소스코드도 깃허브에 올려놓았으니 설정에 어려움이있다면 참고하자 

https://github.com/cokeholic-kim/Univershop

 

GitHub - cokeholic-kim/Univershop

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

github.com

 

반응형
반응형

작년 초에 학원에서 리액트로 쇼핑몰을 만드는 수업을 받았었다.  

다시 리액트를 사용하려고보니 빌드툴도 vite라는 것을 사용하고 상태관리쪽도 새로운 라이브러리가 떠오르고있는듯해서 

다시 연습하는겸 쇼핑몰 프로젝트를 다시만들어보려고한다. 

지금 다니는 회사에서도 리액트 , 타입스크립트 , 넥스트 같은 기술들을 도입하려고 준비하다보니 따라가는 입장에서 연습을 해보고 사용할수있는 api도 회사에서 사용하는 기술인 fastAPI 프레임워크를 통해서 만들어보겠다. 

 

1. 프로젝트 세팅

npm create vite@latest 프로젝트이름

프레임워크와 옵션을 선택할수있게 나올텐데 방향키로 움직여서 원하는 옵션에서 엔터를 누르면 해당 옵션과 프레임워크가 설치된다.

 

나는 리액트 프레임워크에 타입스크립트와 swc를 사용하게 설정해주었다. 

 

이제 vscode로 프로젝트를 열어주고 프로젝트를 실행하기위해 라이브러리를 설치해준다.

npm i

 

라이브러리들이 다 설치되면 잘 실행되는지 확인해본다.

npm run dev

이렇게 running 되었다고 뜰텐데 저 localhost 주소로 들어가보면 페이지가 정상적으로 동작하는지 확인하면 된다.

이렇게하면 리액트 프로젝트를 시작할준비가 끝이났다. 

반응형
반응형

 

 
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를 사용하지 못할수도있다.

 

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

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

 

 

 

 

반응형

+ Recent posts