프론트엔드 데브코스 5기 TIL 39 - SFC(Single File Component)

김영현·2023년 11월 23일
1

TIL

목록 보기
47/129

버전 표기법

major.minor.patch

  • 개편
  • 릴리즈
  • 패치

Node.js와 Npm

node.js는 JS가 동작할수 있게 해주는 환경. 브라우저가 없어도 돌아감.
Npmnode package manager의 약어. JS로 만들어진 패키지들을 관리함.

스크립트 작성 위치

html 다 읽고나서 스크립트 실행하라고 body맨 아래 적는데, 이렇게 하지말고
헤더 안에 선언 후 defer키워드 붙여줌.

<script defer src="./main.js"></script>

async키워드도 비슷한 역할(백그라운드에서 스크립트 다운로드 후 html파싱되면 실행)하지만
여러 script태그를 불러올때 순서를 보장하진 않는다.

-D

개발단계에서만 의존하는 패키지를 설치할때 사용하는 수식어
--save-d와 같은 말이다.

package-lock.json


이렇게 캐럿^기호가 있을땐 그 이상 버전을 아우를 수 있다는 의미다.
=> 환경이 다르면 패키지 버전 차이가 날수있음. 예측하지 못하는 이슈가 발생함. 이를 방지하기 위한게 package-lock.json

즉, package-lock.json은 설치할때의 정확한 버전을 갖고
package.json은 설치할때의 버전의 범위를 갖고있음.
=> 커밋할때 lock파일도 같이해주자~

module.exports

노드환경에서 내보내는 방식. commonJS방식이라 부른다
가져올땐 require로 가져옴.
JS에서사용하는 import,exportESModule방식


SFC with parcel

Single File Component.

html, css, js를 한 파일에 작성할 수 있는 Vue의 기능이다.
file.vue확장자로 만들고 사용함.
하지만 브라우저는 vue파일을 바로 읽지못함. 브라우저가 인식할 수 있게 번들링하는 과정이 필요하다.웹팩을 사용해도 좋지만, 먼저 parcel을 사용하여 번들링 해보자

npm i -D parcel로 개발단계의존성으로 설치하고

  "scripts": {
    "dev": "parcel ./src/index.html"
  },

npm run dev실행하면...

어랍쇼

rm -r .parcel-cache명령어로 캐시날려주고다시해봄


잘보이는구먼 굳!


distribute의 약자 dist.
parcel이 잘 번들링 해주었다.
배포할때 이 폴더를 이용해주면 된다.

빌드

  "scripts": {
    "dev": "parcel ./src/index.html",
    "build": "parcel build ./src/index.html"
  },

요래요래 추가해주고 npm run build해주면..


오류가 난다.

package.jsonmain을 지워주어야함.
=> main은 패키지의 진입점이 되는 모듈인데, 우리는 패키지를 만드는 게 아니라 웹을 만드는거니까 지워주어도 됨.


기존과 다르게 파일이 조금 더 추가되었다.

parcel의 장단점

세세한 조정이 불가능. 쉽고 빠르게 번들링이 가능하다.
=> 초기단계에서 빠르게 만들때 사용 가능. 공부할때도 하면 좋을것 같다.

싱글파일 컴포넌트가 그래서 무어냐?

<script setup>
import { ref } from 'vue'
const greeting = ref('Hello World!')
</script>

<template>
  <p class="greeting">{{ greeting }}</p>
</template>

<style>
.greeting {
  color: red;
  font-weight: bold;
}
</style>

공식문서에 나와있는 예제. 즉 Html, Css, Js를 한군데 묶어서 관리하는 것이다.

왜 사용할까?

정리하자면
1. 한군데 몰아서 작성해서 편하고 컴파일을 한번에 해서 런타임시 컴파일 시간 최적화
2. 자동완성검사할때 편리
3. 컴포넌트마다 스타일 적용할수 있어서 편리
나머지는 공부해가며 천천히 이해해보자.

기본적인 html-css-js구조처럼 생겨서 리액트보단 익숙하구먼


Webpack

저번에 한번 사용해보긴했는데, 상세하게 알진 못하고 사용함.
이번에 잘 알아보자.

웹팩번들러다.
번들은 파일을 압축해주어 용량도 줄이고 http통신 횟수를 줄이는 게 주 목적. 컴파일번들러가 해주는 일이 아니니까 구분하자.

parcel은 그냥 빌드명령어 사용하면 빌드해주고 뚝딱 빌드된 파일이 dist에 저장되었다.
웹팩은 설정이 좀 필요하다.

//node.js의 내장모듈경로를 해석해준다
const path = require('path');

module.exports = {
  	//메인이되는 js파일(모듈 번들링 시작 진입점)
	entry: './src/index.js',
  	//빌드된 파일이 생성될 경로와 이름
  	output:{
		path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
	}
}

이렇게 설정한 파일을 웹팩이 읽어서 적용해준다.

참고로 path.resolve()는 무조건 입력해주는 게 좋다.
=> 웹팩 설정코드가 늘어나다보면 webpack.config.js를 기준으로 경로가 설정되는게 아닐 확률이 높음. 따라서 경로를 확실하게 정해준다.

설치

npm i -D webpack webpack-cli으로 웹팩과 커맨드라인 인터페이스까지 설치해줌. 터미널에서 웹팩을 조종할수 있게 해주는 패키지다.

웹팩 내부 모듈(로더)

웹팩은 JS파일만 해석 가능하다. .vue처럼 다른 파일을 해석하려면 로더라는 기능을 웹팩에 추가해야함.

//webpack.config.js
output...,
module: {
    rules: [
      {
        test: /\.vue$/,
        use: "vue-loader",
      },
    ],
  },

정규식으로 .vue로 끝나는 파일을 모조리 vue-loader의 도움을 받아 웹팩으로 번들링 하겠다는 의미다.

그리고 vue-loader뷰=>JS로 컴파일하는 기능이 따로 없다. 따라서 @vue/compiler-sfc패키지도 설치해주어야함.
참고로 컴파일러와 뷰는 버전이 일치해야함!

또한 플러그인도 등록해주어야한다.

//webpack.config.js
const { VueLoaderPlugin } = require("vue-loader");
...
module...,
plugins: [new VueLoaderPlugin()],

이렇게 하고 npm run build를 하면...


빌드가 잘 되었다. 하지만 웹팩으로 번들링할땐 모드를 명시해주어야한다.(개발, 프로덕트)

빌드된 결과를 보자


js는 잘 되었는데, html파일이 없다! dist폴더에 어떻게 넣어주지?

바로바로...html-webpack-plugin을 사용하면된다.
점점 뭐가 추가되는구먼..

//webpack.config.js
const HtmlPlugin = require("html-webpack-plugin");
...
  plugins: [
    new VueLoaderPlugin(),
    new HtmlPlugin({
      //html파일 경로를 지정.
      //html-webpack-plugin은 내부적으로 path.resolve기능 내장되어있음.
      template: "src/index.html",
    }),
  ],

다시 npm run build명령어를 사용하면!


따란~

참고로 원본 html파일에 srciprt태그가 없더라도, 웹팩이 entry를 이용해 경로를 추가해준다!

다만 dist폴더에 사용자가 직접 필요없는 파일을 만들더라도 그냥 남아있음.
이때 웹팩 설정에서 clean추가해주면됨ㅎㅎ

//webpack.config.js
  output: {
    path: path.resolve(__dirname, "dist"),
    clean: true,
  },

webpack-dev-server

개발서버를 오픈할수 있는 패키지. 패키지를 설치한 뒤

//package.json

  "scripts": {
    "dev": "webpack-dev-server --mode development",
    "build": "webpack --mode production"
  },

이렇게 명령어를 추가해 준뒤 실행해보자!


잘나오는구먼? 참고로 webpack-dev-server를 이용하면 파일 수정사항이 실시간 반영된다!

참고로 웹팩 설정을 바꾸면 서버를 재시작 해주어야함ㅎㅎ

css-loader

아까도 말했듯 웹팩은 js만 인식함. 따라서 CSS도 같이 넘겨주려면...이제 알겠지?

//webpack.config.js
...module
      {
        test: /\.css$/,
        use: ["vue-style-loader", "css-loader"],
      },

배열에 추가할땐 순서가 중요하다. 먼저 해석되어야하는 로더를 제일 나중에작성.

SFC 내부의 스타일

컴포넌트 내부 스타일이 컴파일되면 전역으로 설정됨. 이때 scoped키워드를 스타일 태그에 추가하면, 컴포넌트에만 적용된다.

<style scoped>...</style>

유니크한 id로 스타일이 적용된 노드를 구분짓나보다!
SCSS로 스타일을 작성할 수도 있다. 하지만 로더가 또 필요함!

sass sass-loader를 설치하고...

설정을 요래요래...

//webpack.config.js
      {
        test: /\.s?css$/,
        use: ["vue-style-loader", "css-loader", "sass-loader"],
      },

정규식도 잘다뤄야겠구나...!?

경로별칭

<template>
  <h1>{{ msg }}</h1>
  <Hello />
</template>

<script>
import Hello from "./components/Hello.vue"
export default{
  components:{
    Hello
  },
  data(){
    return{
      msg:"hi webpack!"
    }
  }
}
</script>

<style>
h1{
  color:blue;
}
</style>

지금 Hello라는 컴포넌트를 가져올때 상대경로로 가져오고잇다. 파일 간 오류가 생길 가능성이 있어서 절대경로로 사용하는 게 권장됨. 이를 위해 웹팩의 경로별칭기능 활용하면 됨.

//webpack.config.js
...
  resolve: {
    //확장자 사용하지 않아도 읽어올수 있게.
    extensions: [".vue", ".js"],
      //경로 별칭!
    alias: {
      "~": path.resolve(__dirname, "src"),
    },
  },
  entry: "./src/main.js",

결과는 이렇다

//변경전
import Hello from "./components/Hello.vue"
//변경후
import Hello from "~/components/Hello"

기타 파일 dist에 넣어주기

favicon이나 기타 파일을 dist에 넣어야될때가 있음. 그때 사용하는 패키지가 copy-webpack-plugin

const CopyPlugin = require("copy-webpack-plugin");
...
  plugins: [
	...
    new CopyPlugin({
      patterns: [{ 
        from: "static"
      //to는 output을 참조하기에 생략해도 됨.
      //to: "dist",
      }],      
    }),


static폴더에 있던 favicon이 잘넘어왔다 ㅎㅎ

깃허브로 넘겨줄때 무시하는 파일들!

node_modules/
dist/
.vsocde/ (vsocde 설정파일)

굳!


eslint, prettier 사용하기

vue에서 사용하려면 eslint eslint-plugin-vue설치!
이후 .eslint.js파일만들고...익스텐션설치하고..
다양한 옵션들이 있으니 하나하나 다 따져서 써보는건 의미가 없고! 저번에도 사용해봤고!
필요할때 한번에 쫙 넣어서 사용하겠다!


컴포넌트 등록

Vue가 컴포넌트를 발견하면 구현할 수 있게 위치를 등록해주어야함.

전역 컴포넌트 등록

//main.js
import * as Vue from "vue";
import App from "~/App";
import Btn from "~/components/Btn";

const { createApp } = Vue;
const app = createApp(App);
app.component("Btn", Btn);
app.mount("#app");

createApp().component("사용자 지정 이름", 컴포넌트)
이렇게 하면 어떤 컴포넌트에서든 사용할 수 있는 전역컴포넌트가 된다.

<template>
<h1>Hello vue !</h1>
<Btn></Btn>
</template>

<script>
export default{
}
</script>

import하지않아도 사용할 수 있음.
다만 전역 컴포넌트는 트리 쉐이킹(사용하지 않는 코드 제거)등의 번들링 기능을 활용할 수 없다.
또한 의존관계를 명시적으로 만들지 못함.
=> 최대한 전역 컴포넌트 사용을 줄인다. 자주 사용하는 버튼컴포넌트 같은 경우에는 전역으로 사용하기도 함

지역 컴포넌트 등록

import키워드로 갖고와서 components옵션에 등록.

<template>
<h1>Hello vue !</h1>
<Btn></Btn>
<Hello/>
</template>

<script>
import Hello from '~/components/Hello';
export default{
  components:{
    Hello
  }
}
</script>

지금까지 사용한 방식이 지역컴포넌트다.


컴포넌트의 Props

원시값들은 기냥 전달해주면 되는데, 객체전달할때는 프로퍼티 별로 전달하지 않고 한번에 전달하는 방법도 있다.

post:{
	id:1,
    title:"나 제목이오"
}

<blog-post v-bind="post"></blog-post>
<!-- 위, 아래 둘 다 같은 의미다 -->
<blog-post :id="post.id" :title="post.title"></blog-post>

데이터흐름은 단방향

업로드중..
MVVM패턴. 뷰와 100%일치하지는 않는다

이렇게 생겼지만, 모든 props는 부모=>자식 단방향 바인딩을 형성한다. 그래서 readonly가 되어있음.

그라믄 바꿔서 사용하려면 어떡해야하나?

  • 자기 상태값에 저장해두고 쓰기
  • computed속성 정의해두고 쓰기

간단하구먼?

props 유효성 검사

  props:{
    message:String,
    name:[String,Number],
 	email:{
    	required:true
    	type:String,
        default:"디폴트 이메일값"
    },
  },
  props2:{
      validator: function(value){
   		//값이 꼭 아래 배열과 일치해야함
    	return['a', 'b'].indexOf(value) !== -1
    }

프롭스로 받아올때 타입을 지정해줄수 있다. 신기허구먼?
참고로 배열은 유니온타입(or)과 똑같다.
default는 기본값을 설정하고 프롭스를 덮어쓴다.(참조형 데이터로 들어오면 함수로 리턴해주어야함) required는 무조건 있어야 하는 값.
이거 완전 mongodb스키마와 비슷하구먼

Prop 대소문자 구분

HTML속성명은 대소문작 구분없이 소문자로 해석
=> html속성으로 작성할땐 케밥-케이스로 해야함.

<a v-bind:post-title></a>
<script>
...
data(){
	return{
    postTitle:....
    }
}
</script>

컴포넌트 Non-prop 속성

컴포넌트 내부 요소가 여러개일땐 그냥 class="name"이런식으로 할당할 수 없음

<!-- Hello.uve -->
<template>
  <h1 
  :style="$attrs.style" 
  :class="$attrs.class">hello</h1>
  <h2 @click="$attrs.onClick">dsadasd</h2>
</template>

<!-- App.vue -->
<template>
  <h1>{{ msg }}</h1>
  <Hello @click="msg += '!' " style="font-size: 100px" class="hello"/>
</template>

요래요래 따로 전달 가능하다. 여기서 $attrs는 뭘까?
=> props로 전달한 값을 제외한 객체!
이를 Non-prop 속성이라함.

또한 inheritAttrs:false옵션을 script내부에 적어주면 프롭스를 제외한 값을 가져오지 않는다.


느낀점

양이 굉장히 많은듯 싶다가도 이해가 잘 되었다.
프롭스가 단방향이라는게 아주 맘에들었다. 자식이 바꿔버리면 예측 불가능한 사이드이펙트가 자주 발생할것같음.
리액트를 아주 조금 해본 경험 + 노션클론 리팩토링을 진행하며 얻은 경험(이게 아주 값진것 같음)이 도와주지 않았나 싶다.
물론 지금JS를 사용할때처럼 능숙하려면 시간이 걸리겠지만...이전보다 학습속도가 빨라진건 좋은 징조다
뷰가 쉬워서 그런걸 수도 있음..!😯

다만 웹팩,린트 부분은 중요한 설정은 알아놔야할것 같다. 다 외우는건 비효율적이고...
아 왜이리 패키지가 많이 필요하지..?ㅋㅋㅋㅋ

profile
모르는 것을 모른다고 하기

0개의 댓글