React(Component & CSS) +3

LEE EUI JOO·2023년 2월 8일
0

Web Programming

목록 보기
12/17

Ref

  • 리액트에서 컴포넌트를 참조하거나 변수를 만들고자 할 때 사용

  • HTML 에서는 DOM 을 선택하고자 할 때는 id를 부여한 후 getElementByid 함수를 이용하거나 querySelector 라는 함수에 선택자를 대입해서 선택

  • HTML 에서 입력된 값이나 모양을 선택하기 위해 DOM 객체를 찾아오지만 리액트에서는 입력된 값은 state를 이용해서 사용하기 때문에 DOM 객체를 선택할 사항이 그리 많지 않다


사용방법

  • ref 이름 = React.createRef()
  • <태그 ref = {thid.ref이름} />

새 프로젝트 생성

  • ref 사용

    • css 파일 생성 - src/RefComponent.css

    • 클래스 컴포넌트 생성 - src/RefComponent.jsx

Component.css

.success{
    background-color: lightgreen;
}

.failure{
    background-color: lightcoral;
}

RefComponent.jsx

import React, {Component} from 'react'

import './RefComponent.css'

class RefComponent extends Component{
    //다른 객체를 잠조할 수 있는 변수를 생성

    input = React.createRef();




    //state : 변경 가능한 데이터, state의 값이 변경되면 Component는 자동으로
    //rerendering (재출력)
    //클래스 형 컴포넌트에서는 생성자(this.state로 생성)나 함수 외부에서 생성이 가능하다
    //자바와 같은 객체 지향 언어에서는 메서드 내에서 변수를 호출하면
    //메서드 내에서 찾고 없으면 클래스 그래도 없으면 상위 클래스로 찾아간다
    //파이썬 혹은 자바스크립에서는 함수 내에서 변수를 사용했는데 없으면 생성한다
    //파이썬 이나 자바스크립트에서는 인스턴스가 소유하는 변수를 만들 때나 사용할 때
    //this 를 붙여야 한다


    state = {

        password:'',
        clicked: false,
        validated : false

    }

    //이번트 함수
    handleChange = (e) => {
        // 이벤트가 발생한 객체에 설정된 값을 password라는 state에 대입
        this.setState({
            password:e.target.value
        })
    }


    handleClick = (e) => {

        this.setState({
            clicked:true,
            validated: this.state.password === '0000'
        })

        /*let input  = document.getElementById('input');
        input.focus();*/


        //input 이라는 ref 가 참조하는 객체에 포커스를 설정
        this.input.current.focus();
    }

    

    render(){
        return(
            <>
                <input 
                id = 'input'
                ref = {this.input}
                type='password' 
                onChange = {this.handleChagne} 
                value = {this.state.password}
                className = {this.state.clicked ? this.state.validated ? 'success' : 'failure':''}/>

                <button onClick={this.handleClick}>검증</button>
            
            
            
            
            </>
        )
    }
}

export default RefComponent;

App.js

import './App.css';
import RefComponent from './RefComponent';

function App() {
  return (
    <>
    
    <RefComponent/>
    
    </>
    
  );
}

export default App;

  • Ref 를 설정하면 컴포넌트 내부의 메서드나 속성도 호출이 가능하다

배열에 존재하는 함수 - 이름이 모든 프로그래밍 언어에서 동일

  • map
    • 배열을 순회하면서 콜백 함수를 실행해서 리턴한 결과를 배열로 만들어서 리턴하는 함수
    • 목적은 데이터를 변환하고자 할 때 이용
    • 콜백 함수의 모양
      • 3개의 매개변수를 가질 수 있고 반드시 하나의 값을 리턴해야 한다
      • 첫번째 매개변수는 배열의 요소
      • 두번째 매개변수는 요소의 인덱스
      • 세번째 매개변수는 배열
  • filter
    • 배열을 순회하면서 콜백 함수를 실행해서 리턴한 결과가 true인 데이터만 배열로 만들어서 리턴하는 함수
    • 목적은 데이터를 추출하고자 할 때 이용
    • 콜백 함수의 모양
      • 3개의 매개변수를 가질 수 있고 반드시 bool 값을 리턴해야 한다
      • 첫번째 매개변수는 배열의 요소
      • 두번째 매개변수는 요소의 인덱스
      • 세번째 매개변수는 배열
  • reduce
    • 배열을 순회하면서 콜백 함수를 실행해서 리턴한 결과를 누적하는 함수
    • 함수를 호출할 때 콜백 함수와 초기값을 설정
    • 콜백 함수의 모양 - 4개의 매개변수를 가질 수 있고 하나의 값(숫자)를 리턴해야 한다
      • 첫번째 매개변수는 리턴 한 값들의 누적 값
      • 두번째 매개변수는 배열의 요소
      • 세번째 매개변수는 요소의 인덱스
      • 네번째 매개변수는 배열

RefComponent.jsx

import React, {Component} from 'react'

import './RefComponent.css'

class RefComponent extends Component{
    constructor(props){
        super(props);

        let ar = [10,20,30,40];
        //배열을 순회하면서 1을 더해보자

        let result = ar.map((element, index, br) => {return element + 1});
        console.log(result);
    }
    
    //다른 객체를 잠조할 수 있는 변수를 생성

    input = React.createRef();


    //state : 변경 가능한 데이터, state의 값이 변경되면 Component는 자동으로
    //rerendering (재출력)
    //클래스 형 컴포넌트에서는 생성자(this.state로 생성)나 함수 외부에서 생성이 가능하다
    //자바와 같은 객체 지향 언어에서는 메서드 내에서 변수를 호출하면
    //메서드 내에서 찾고 없으면 클래스 그래도 없으면 상위 클래스로 찾아간다
    //파이썬 혹은 자바스크립에서는 함수 내에서 변수를 사용했는데 없으면 생성한다
    //파이썬 이나 자바스크립트에서는 인스턴스가 소유하는 변수를 만들 때나 사용할 때
    //this 를 붙여야 한다


    state = {

        password:'',
        clicked: false,
        validated : false

    }

    //이번트 함수
    handleChange = (e) => {
        // 이벤트가 발생한 객체에 설정된 값을 password라는 state에 대입
        this.setState({
            password:e.target.value
        })
    }


    handleClick = (e) => {

        this.setState({
            clicked:true,
            validated: this.state.password === '0000'
        })

        /*let input  = document.getElementById('input');
        input.focus();*/


        //input 이라는 ref 가 참조하는 객체에 포커스를 설정
        this.input.current.focus();
    }

    

    render(){
        return(
            <>
                <input 
                id = 'input'
                ref = {this.input}
                type='password' 
                onChange = {this.handleChange} 
                value = {this.state.password}
                className = {this.state.clicked ? this.state.validated ? 'success' : 'failure':''}/>

                <button onClick={this.handleClick}>검증</button>
            
            
            
            
            </>
        )
    }
}

export default RefComponent;

  • map 함수

RefComponent.jsx

let ar = ['aaa','bbb','ccc','ddd'];
        //배열을 순회하면서 1을 더해보자

        let result = ar.map((element, index, br) => {return element.substring(0,2) + '...'});
        console.log(result);

  • filter 함수

RefComponent.jsx

// true 를 리턴한 데이터만 골라서 배열을 생성
        result = ar.filter((element, index, br)=> {
            return element.length > 2
        });
        console.log(result);

  • sort - 웹에서 정렬할때 쓰임! 서버에서는 orderby 함수 적용 불가!

RefComponent.jsx

ar = [200, 100, 400, 40];
        //자바스크립트의 배열을 정렬하면 모두 문자열로 변환해서 크기 비교
        //숫자의 오름차순 정렬을 하고자 하는 경우는 2개의 매개변수를 가지고
        //정수를 리턴하는 함수를 대입해야한다
        //잎의 데이터가 크면 양수 같으면 0 작으면 음수를 리턴하면 된다.
        
        ar.sort((a,b) => {return a-b;});
        console.log(ar)


Component 반복

동일한 컴포넌트가 하나의 화면에 여러 개 배치되었을 때 컴포넌트를 구분하기 위한 속성

  • Key
    • 동일한 컴포넌트를 반복적으로 매치할 때 key를 설정하지 않으면 콘솔창에 에러가 발생
    • 대부분은 일련번호 형태로 설정한다
  • 여러개 컴포넌트 반복 출력
    • 동일한 컴포넌트를 여러 개 출력하는 컴포넌트 - literationComponent.jsx (함수형 컴포넌트)
  • Apps.js 에서 화면 출력

LiterationComponent.jsx

const LiterationComponent = () => {
    
    const names = ['ab','cd','ef','gh'];
    const namesList = names.map(
        (name,index)=>{return <li key = {index}> {name}</li>})

    return(
        
        <ul>

            {namesList}

        </ul>


    )
}

export default LiterationComponent;

App.js

import './App.css';
import RefComponent from './RefComponent';

import LiterationComponent from './LiterationComponent';
function App() {
  return (
    <>
    
    <LiterationComponent/>
    <RefComponent/>
    
    </>
    
  );
}

export default App;

  • 클래스, 파일, 함수의 이름은 첫글자는 대문자로 하자!!
  • 실행 한 후 콘솔 창 확인
    • 화면에 출력은 되지만, list는 유일한 값인 key가 있어야 한다고 에러를 뿜음

배열에 데이터를 추가하거나 배열에서 데이터를 삭제

  • 데이터 추가

    • 배열에 데이터를 추가하는 함수는 push 가 제공되는데 push는 원본 배열에 데이터를 추가한다
    • 다른 배열이나 원소를 결합하는 함수로 concat 가 제공되는데 이 함수는 원본은 그대로 두고 기존 배열을 복사해서 배열이나 원소를 결합해서 리턴한다
    • 문자열 리터럴이 동작하는 원리 와 concat 이 동일함!
    • 리터럴은 절대로 소멸되지 않는 static 영역에 메모리를 할당한다
  • 데이터 삭제

  • 배열의 데이터를 삭제할 때 delete 사용이 가능한데 delete는 원본 배열에서 요소를 삭제한다

  • slice 와 concat의 조합 또는 filter를 이용하여 특정 데이터를 제거한 배열을 생성하는 것이 가능

추가

LiterationComponent.jsx

import React,{Component} from "react";
class LiterationComponent extends Component {
    state = {

        names: ['나','는','낭','만','고','양'],
        name:''

    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]:e.target.value
        })
    }

    handleClick = (e) => {
        this.setState({

            names:this.state.names.concat(this.state.name),
            name:''
        })
    }

    render(){
        const nameList = this.state.names.map(

            (name,index) => {return <li key={index}>{name}</li>});
        return (

            <>
                <input value = {this.state.name} onChange = {this.handleChange}
                name = 'name'/>
                <button onClick={this.handleClick}>추가</button>
                {nameList}

            </>
        )
    }
}

export default LiterationComponent;

제거 - 더블클릭했을 때

  • 더블 클릭 확인

LiterationComponent.jsx

import React,{Component} from "react";
class LiterationComponent extends Component {
    state = {

        names: ['나','는','낭','만','고','양'],
        name:''

    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]:e.target.value
        })
    }

    handleClick = (e) => {
        this.setState({

            names:this.state.names.concat(this.state.name),
            name:''
        })
    }

    handleRemove = (e) => {
        alert('더블 클릭')
    }

    render(){
        const nameList = this.state.names.map(

            (name,index) => {return <li key={index}
                onDoubleClick={this.handleRemove}>{name}</li>});
        return (

            <>
                <input value = {this.state.name} onChange = {this.handleChange}
                name = 'name'/>
                <button onClick={this.handleClick}>추가</button>
                {nameList}

            </>
        )
    }
}

export default LiterationComponent;

  • 누가 더블클릭 됐는지는 알 수 없다
    LiterationComponent.jsx
import React,{Component} from "react";
class LiterationComponent extends Component {
    state = {

        names: ['나','는','낭','만','고','양'],
        name:''

    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]:e.target.value
        })
    }

    handleClick = (e) => {
        this.setState({

            names:this.state.names.concat(this.state.name),
            name:''
        })
    }
    //반복문으로 생성한 컴포넌트에 이벤트를 연결할 때는
    //몇 번째 데이터에게 발생한 것인지 알 수 있도록 index 혹은 id를 넘겨줘야함
    handleRemove = (index) => {
        alert(index)
    }

    render(){
        const nameList = this.state.names.map(

            (name,index) => {return <li key={index}
                onDoubleClick={() => this.handleRemove(index)}>{name}</li>});
        return (

            <>
                <input value = {this.state.name} onChange = {this.handleChange}
                name = 'name'/>
                <button onClick={this.handleClick}>추가</button>
                {nameList}

            </>
        )
    }
}

export default LiterationComponent;

LiterationComponent.jsx

import React,{Component} from "react";
class LiterationComponent extends Component {
    state = {

        names: ['나','는','낭','만','고','양'],
        name:''

    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]:e.target.value
        })
    }

    handleClick = (e) => {
        this.setState({

            names:this.state.names.concat(this.state.name),
            name:''
        })
    }
    //반복문으로 생성한 컴포넌트에 이벤트를 연결할 때는
    //몇 번째 데이터에게 발생한 것인지 알 수 있도록 index 혹은 id를 넘겨줘야함
    //구조 분해 할당 이용해서 코드 정리
    handleRemove = (index) => {
        const {names} = this.state;
        this.setState({
            names:names.slice(0,index).concat(
                names.slice(index +1 , names.length)
            )
        })
    }

    render(){
        const nameList = this.state.names.map(

            (name,index) => {return <li key={index}
                onDoubleClick={() => this.handleRemove(index)}>{name}</li>});
        return (

            <>
                <input value = {this.state.name} onChange = {this.handleChange}
                name = 'name'/>
                <button onClick={this.handleClick}>추가</button>
                {nameList}

            </>
        )
    }
}

export default LiterationComponent;

  • '나', '는' 더블 클릭

변수가 가리키는 데이터를 다른 변수에 대입하는 경우

LiterationComponent.jsx

this.state.names.slice
----
const {names} = this.state;
선언 후
names.slice 로 수정
  • 지역 변수의 데이터를 다른 곳에서 사용하기 위함

  • 코드를 간결하게 하거나 접근을 빠르게 하기 위함

  • [인덱스] 또는 .이름을 이용해서 접근을 하게 되면 기준점을 찾아간 후 데이터를 찾아감

  • 자주 사용하는 데이터는 인덱스 혹은 .이름이 아닌 직접 접근하도록 하면 코드도 간결해지고 접근 속도도 빨라지는 장점이 있음

slice 보다 빠른 filter 함수 사용의 예

LiterationComponent.jsx

import React,{Component} from "react";
class LiterationComponent extends Component {
    state = {

        names: ['나','는','낭','만','고','양'],
        name:''

    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]:e.target.value
        })
    }

    handleClick = (e) => {
        this.setState({

            names:this.state.names.concat(this.state.name),
            name:''
        })
    }
    //반복문으로 생성한 컴포넌트에 이벤트를 연결할 때는
    //몇 번째 데이터에게 발생한 것인지 알 수 있도록 index 혹은 id를 넘겨줘야함
    handleRemove = (index) => {
        //slice 함수를 이용해서 선택한 항목 앞 까지 와 선택한 항목 뒤의 내용을 합치기
        const {names} = this.state;
        /*
        this.setState({
            names:names.slice(0,index).concat(
                names.slice(index +1 , names.length)
            )
        })*/
        this.setState({

            names: names.filter((element, i ,ar) => {

                return i !== index;
            })
        })
    }

    render(){
        const nameList = this.state.names.map(

            (name,index) => {return <li key={index}
                onDoubleClick={() => this.handleRemove(index)}>{name}</li>});
        return (

            <>
                <input value = {this.state.name} onChange = {this.handleChange}
                name = 'name'/>
                <button onClick={this.handleClick}>추가</button>
                {nameList}

            </>
        )
    }
}

export default LiterationComponent;


참고 - 이터레이션

☑️ iter

  • iterator(반복자)

    • 배열[인덱스] 형태로 접근하는 방식

    • 배열은 배열의 시작 위치를 참조

    • 배열[3] -> 배열의 시작 위치를 찾고 그 다음에 3칸을 건너뛰어 데이터를 찾는 방식

  • 이터레이션을 이용한 접근

    • 첫번째 데이터를 찾고 포인터가 찾은 데이터를 가리키고 있음

Class Component 의 Life Cycle(수명주기)

Component의 수명 주기

  • Mount (컴포넌트가 화면에 출력) -> Update(props 나 state 의 변경 등으로 리랜더링) -> Unmount(소멸)

  • Mount 될 때 호출되는 메서드

    • state의 초기화 설정을 여기서 수행

    • constructor : 생성자, 클래스가 인스턴스를 만들 때 호출되는 메서드

    • getDeriveedStateFromProps : props에 있는 값을 state에 동기화 시키는 메서드

    • render : UI 를 랜더링 하는 메서드

      • 필수 구현 메서드

      • 모양을 정의

      • 컴포넌트 내부의 구성 요소를 가져오거나 수정하는 작업은 안됨

    • componentDidMount : 웹 브라우저에 컴포넌트가 출력된 후 호출되는 메서드

      • 다른 자바스크립트 라이브러리를 호출하고 이벤트 등록, 타이머 사용, 네트워크 요청 등의 작업을 수행
      • 리액트나 앵귤러 또는 뷰 와 같은 SPA 프레임워크에서는 화면을 먼저 출력하고 데이터를 요청 - 스플래시 이미지
  • Update 될 때 호출되는 메서드

    • getDerivedStateFromProps : 상위 컴포넌트가 리랜더링 되거나 props 에 변화가 생기면 호출

    • shouldComponentUpdate : state 가 변경되면 호출되는 메서드로 이 메서드에서 false를 리턴하면 리랜더링을 하지 않음

      • 애플리케이션 성능을 최적화 할 때 상황에 맞는 알고리즘을 작성해서 리랜더링을 방지할 목적으로 사용됨
    • render : forceUpdate 라는 함수를 호출해서 강제로 reder를 호출할 수 있음 (트리거)

    • getSnapshotBeforeUpdate : 이전 내용을 수정하기 전에 호출

      • 2개의 매개변수가 전달되는데 첫번째 매개변수는 업데이트 되기 전의 props 이고, 두번째 매개변수는 엡데이트 되기 전의 state 이다
      • 업데이트 되기 전의 값이 필요할 때 사용되는 메서드
    • componentDidUpdate : 컴포넌트가 다시 출력되고 난 후 호출되는 메서드

  • Unmount 될 때 호출되는 메서드

    • componentWillUnmount : DOM에서 제거될 때 호출되는 메서드
      • componentDidMount 에서 등록한 이벤트, 타이머 등을 정리

LiterationComponent.jsx

import React,{Component} from "react";
class LiterationComponent extends Component {

    constructor(props){
        super(props);
        console.log('생성자');
    }

    componentDidMount(){
        console.log('컴포넌트가 화면에 출력된 후 호출되는 메서드');
    }

    componentDidUpdate(prevProps,prevState,snapshot){
        console.log('컴포넌트가 재 출력된 후 호출되는 메서드');
        console.log('props:' + prevProps);
        console.log('state:' + prevState);
        console.log('과거의 값:' + snapshot);

        
    }

    state = {

        names: ['나','는','낭','만','고','양'],
        name:''

    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]:e.target.value
        })
    }

    handleClick = (e) => {
        this.setState({

            names:this.state.names.concat(this.state.name),
            name:''
        })
    }
    //반복문으로 생성한 컴포넌트에 이벤트를 연결할 때는
    //몇 번째 데이터에게 발생한 것인지 알 수 있도록 index 혹은 id를 넘겨줘야함
    handleRemove = (index) => {
        //slice 함수를 이영해서 선택한 항목 앞 까지 와 선택한 항목영역
        const {names} = this.state;
        /*
        this.setState({
            names:names.slice(0,index).concat(
                names.slice(index +1 , names.length)
            )
        })*/
        this.setState({

            names: names.filter((element, i ,ar) => {

                return i !== index;
            })
        })
    }

    render(){
        const nameList = this.state.names.map(

            (name,index) => {return <li key={index}
                onDoubleClick={() => this.handleRemove(index)}>{name}</li>});
        return (

            <>
                <input value = {this.state.name} onChange = {this.handleChange}
                name = 'name'/>
                <button onClick={this.handleClick}>추가</button>
                {nameList}

            </>
        )
    }
}

export default LiterationComponent;

개발 모드에서의 출력

  • 개발 모드에서는 컴포넌트가 2번씩 랜더링 - StrictMode로 수행되기 때문

  • 컴포넌트를 1번만 랜더링 하는 방법

    • strictMode 를 해제 - index.js 에서 Strict 태그를 제거
    • 운영 모드로 실행
  • index.js 에서 strictMode를 제거

  • 출력 결과 - 1번만 랜더링

  • 운영 모드로 실행

    • npm run build - 빌드

    • npm install -g serve - 웹서버를 설치

    • serve -s build


여러 개의 동일한 컴포넌트 반복 출력

  • 컴포넌트 생성 - Commnet.jsx
    Commnet.jsx
import React from "react";
//별도의 css 파일을 만들지 않고 내부에 스타일을 설정할 때는 주의 해야함
//css의 속성이 camel case로 이름이 변경된다.
// - 기호도 없어짐

//예) background-color : backgroundColor 로 바뀜
// 스타일을 객체 형태로 생성해서 설정

const styles = {
    wrapper:{
        margin:8,
        padding:8,
        display:'flex',
        felxDirection:'row',
        border:'1px solid gray',
        borderRadius: 16

    },
    imageContainer:{},
    image:{
        width:50,
        height:50,
        borderRadius:25
    },
    contentContainer:{
        marginLeft:8,
        display:'flex',
        felxDirection:'column',
        justifyContent: 'center'
    },
    nameText:{
        color:'black',
        fontSize:16,
        fontWeight:'bold'

        
    },
    commentText:{
        color:'black',
        fontSize: 16
    }
}

function Comment(props){
    return(
        <>
            <div style={styles.wrapper}>
                <div style={styles.imageContainer}>
                    <img src = 
                            'https://upload.wikimedia.org/wikipedia/commons/8/89/Portrait_Placeholder.png'
                            style={styles.image}
                            alt='이미지를 다운로드 할 수 없당'/>

                </div>
                
            </div>
            <div style={styles.contentContainer}>
                <span style={styles.nameText}>{props.name}</span>
                
                <span style={styles.commentText}>{props.comment}</span>
                
            </div>
                
        
        </>

    )


}

export default Comment;
  • CommentList 생성 - CommentList.jsx

CommentList.jsx

import React from "react";
import Comment from "./Comment";

//출력할 데이터 생성

const comments = [

    {

        name: 'lee',
        comment: 'hello'
    },

    {

        name: 'eui',
        comment: 'bonjur'
    },

    {

        name: 'joo',
        comment: 'nihao'
    },
]

function CommentList(props){

    return(
        <>
        
        {
            comments.map((comment,index,ar) => {
                return (<Comment name={comment.name}
                    comment={comment.comment}/>);

            })

        }
        
        
        </>
    )
}
export default CommentList;
  • App.js에 적용

App.js

import './App.css';

import RefComponent from './RefComponent';

import LiterationComponent from './LiterationComponent';

import CommentList from './CommentList';


function App() {
  return (
    <>
    
    <LiterationComponent/>
    <RefComponent/>
    <CommentList/>
    
    </>
    
  );
}

export default App;


스타일링 (Styling)

  • 일반 CSS 사용

    • webpack의 css-loader 가 css를 호출해서 적용
    • 이름 규칙
      • 클래스 이름 앞에 컴포넌트 이름을 추가해서 생성 : App-header(App 이라는 컴포넌트에 header 라는 클래스)
      • 접두사를 붙이지 않고 이름을 지울 때 일종의 규칙을 설정해서 클래스가 어디에서 어떤 용도로 사용되는지 작성하는 방식 - BEM(Block Element Modifier)방식
        • .card_title_primary
  • 제공되는 css

    • index.css
    • App.css
  • 외부 CSS 라이브러리 이용

    • 리액트는 index.html 파일위에 App.js를 이용해서 화면 출력을 수행한다.
      • 모든 컴포넌트는 index.html 파일의 출력 위에 놓이게 된다
      • index.html 에 설정된 CSS는 앱의 모든 컴포넌트에 영향을 미칠 수 있다
      • 앱 내에서 계속해서 사용할 css 나 js 가 있다면 index.html에 링크를 설정하면 된다.

부트스트랩 적용

  • Bootstrap.jsx 파일 생성

Bootstrap.jsx

export default function Bootstrap(props){

    return(
        <form>
            <div className="mb-3">
                <label htmlFor="exampleInputEmail1" className="form-label">
                    Email Address
                </label>
                <input type='email' className="form-control"
                id = 'exampleInputEmail1' area-aria-describedby="emailHelp" />

                <div id = 'emailHelp' className="form-text">
                    이메일을 절대로 다른 용도로 사용하지 말것.
                </div>
                <button type='submit' className="btn btn-primary">
                    로그인
                </button>
            </div>



        </form>
    )
}
  • App.js 수정

App.js

import './App.css';

import Bootstrap from './Bootstrap';


function App() {
  return (
    <>
    
      <Bootstrap/>
    
    </>
    
  );
}

export default App;

부트스트랩을 적용

  • public/index.html 파일에 부트스랩 링크 삽입
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">


구글의 머터리얼 아이콘 세트 사용 - import를 사용

@import url('https://fonts.googleapis.com/icon?family=Material+Icons');

.material-icons{
  font-family: 'Material Icons';
  display: inline-block;
}
  • 화면에 직접 출력될 컴포넌트를 저장할 pages 디렉토리를 src 디렉토리에 생성

  • pages 디렉토리에 아이콘을 출력할 컴포넌트 파일을 생성 - Icon.jsx

Icon.jsx

export default function Icon(){

    return(
        <>
            <h3>Icon</h3>
            <span className="material-icons">home</span>
            <span className="material-icons">check_circle_outline</span>
        
        
        </>
    )
}
  • App.js 파일에서 icon 컴포넌트를 출력

App.js

import './App.css';

import Bootstrap from './Bootstrap';
import Icon from './pages/Icon';


function App() {
  return (
    <>
      <Icon/>
      <Bootstrap/>
    
    </>
    
  );
}

export default App;

  • 이전 방법의 문제점

    • 다른 사이트의 호스팅 된 외부 CSS 파일을 사용하는데 이 방식은 네트워크에 영향을 받을 수 있음

    • 따라서 설치를 해서 사용하게 되면 설치할 때만 네트워크가 연결되어 있으면 된다.

    • 설치 방법 : npm i @fontsource/글꼴이름

  • 폰트 설치 : npm i @fontsource/material-icons

  • 화면에 출력될 컴포넌트의 부품이 될 컴포넌트를 저장하기 위한 components 디렉토리를 생성

  • components 디렉토리에 아이콘 1개를 출력하기 위한 컴포넌트를 생성 - Icon.jsx

Icon.jsx

export default function Icon(){

    return(
        <>
            <h3>Icon</h3>
            <span className="material-icons">home</span>
            <span className="material-icons">check_circle_outline</span>
        
        
        </>
    )
}
  • components 디렉토리의 모든 내용을 export하는 index.js 파일을 생성하고 작성

    • index.js는 디렉토리를 import 하면 import 되는 파일로 이름은 고정

index.js

export * from './Icon';
  • pages 디렉토리에 화면에 출력될 컴포넌트를 가진 Usingicon.jsx 를 생성하고 작성

Usingicon.jsx

// 디렉토리 이름을 기재하면 index.js 에서 export한 데이터를 가져온다

import {Icon} from '../components';
export default function UsingIcon(){
    return(
        <>
        
        <h3>설치한 아이콘</h3>
        <Icon name='home' style={{color:'blue'}} />
        <Icon name='check_circle_outline' style={{color:'red',fontSize:'70px'}} />
    
        </>
    )
}
  • App.js 파일을 수정해서 화면에 출력

App.js

import './App.css';

import Bootstrap from './Bootstrap';
import Icon from './pages/Icon';
import UsingIcon from './pages/Usingicon';

function App() {
  return (
    <>
      <UsingIcon/>
      <Icon/>
      <Bootstrap/>
    
    </>
    
  );
}

export default App;
  • 아이콘 불러온 결과


CSS Module

개념

  • css 를 불러와서 사용할 때 webpack 이 css-loader를 이용해서 고유한 이름을 만들어 사용하도록 해주는 기술

  • 클래스 이름이 중첩되는 현상을 방지해 준다

    • [파일이름][클래스이름][해시값] 의 형태로 고유한 클래스 이름을 만들어 준다

    • 모든 곳에서 사용할 수 있는 클래스를 만들 대는 앞에 :global 을 추가해주면 된다


css 파일을 생성해서 적용

  • src 디렉토리에 css 내용을 작성할 CSSModule.module.css 파일 생성

CSSModule.module.css

.wrapper{
    background: black;
    padding: 1em;
    color: white;
    font-size: 2rem;

}

:global .something{
    font-weight: 800;
    color: aquamarine;
}
  • style을 사용할 컴포넌트를 생성 - CSSModule.jsx 파일 생성

CSSModule.jsx

import styles from './CSSModule.module.css';

export default function CSSModule (){
    return (
        <div className={styles.wrapper}>
            Hi! I'm <span className='something'>CSS Module~!</span>
        </div>
    )
}
  • App.js 를 수정하여 출력

App.js

import './App.css';

import Bootstrap from './Bootstrap';
import Icon from './pages/Icon';
import UsingIcon from './pages/Usingicon';
import CSSModule from './CSSModule';

function App() {
  return (
    <>
      <CSSModule/>
      <UsingIcon/>
      <Icon/>
      <Bootstrap/>
    
    </>
    
  );
}

export default App;


여러 개의 클래스 적용 - " ` " 이용 (mac 에서는 ₩ 한글)

  • 문자열 템플릿(문자열을 생성할 때 다른 데이터를 포함시켜 생성) 사용
  • CSSModule.module.css 파일에 클래스를 추가

CSSModule.module.css

.wrapper{
    background: black;
    padding: 1em;
    color: white;
    font-size: 2rem;

}

.inverted{
    color: black;
    background: white;
    border: 1px solid burlywood;
}

:global .something{
    font-weight: 800;
    color: aquamarine;
}
  • CSSModule.jsx 파일을 수정

CSSModule.jsx

import styles from './CSSModule.module.css';

export default function CSSModule (){
    return (
        <div className={`${styles.wrapper} ${styles.inverted}`}>
            하이 HI ! I'm <span className='something'>CSS Module~!</span>
        </div>
    )
}


classnames 라이브러리

  • 여러 개의 클래스 이름을 중첩해서 사용하거나 조건부로 사용할 때 편리한 클래스 이름 관련 라이브러리

  • 외부 라이브러리라서 설치 필요

    • 터미널에서 설치 : npm i classnames (yarn add classnames)
    • 임포트 : import classNames from 'classnames'
  • 사용 방법

    • import classNames from 'classnames/bind'

    • const cx = className.bind(스타일 객체);

      • cx(클래스 이름 나열) : 공백을 추가해서 설정
      • cx(클래스 이름) : true or false
  • CSSModule.jsx 를 수정

CSSModule.jsx

import styles from './CSSModule.module.css';

import classNames from 'classnames/bind';

const cx = classNames.bind(styles);

export default function CSSModule (){
    return (
        <div className={cx('wrapper', 'inverted')}>
            하이 HI ! I'm <span className='something'>CSS Module~!</span>
        </div>
    )
}

CSS Preprocessor (전처리기)

개념

  • preprocessor : 원래의 기능이 동작하기 전에 수행되는 기능

  • css preprocessor : css 의 불편함을 해소하기 위해서 등장 (css 작성이 생각보다 어려움)

  • 선택자의 중첩 문제나 조건문, 반복문 등의 사용을 위해서 활용

  • webpack 이 필요한 이유가 이 부분 때문

  • 대표적인 전처리기로는 Sass, Less, Sylus 등이 있음


Sass

  • scss : sass 의 모든 기능을 지원하는 superset
    • 설치 : node-scss scss-loader sass
  • 적용 : 필요한 패키지 설치
    • npm install node-scss scss-loader sass

❗️❗️❗️ node-scss 패키지 설치 오류 ❗️❗️❗️

$ npm uninstall node-scss
$ npm install -D node-sass
  • CSSModule.module.css 파일의 확장자를 scss 로 변경하고 내용 수정
/* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용가능*/
.wrapper {
    background: black;
    padding: 1rem;
    color: white;
    font-size: 2rem;
    //inverted 가 wapper 와 같이 사용되는 경우만 적용
    & .inverted {
      color:black;
      background:white;
      border: 1px solid black;
    }
  }
  /* 글로벌 CSS 작성 */
  :global {
    .something{
      font-weight: 800;
      color: aqua;
    }
  }
  • CSSModule.jsx 파일의 import 구문 수정
import styles from './CSSModule.module.scss';

import classNames from 'classnames/bind';

const cx = classNames.bind(styles);

export default function CSSModule (){
    return (
        <div className={cx('wrapper', 'inverted')}>
            하이 HI ! I'm <span className='something'>CSS Module~!</span>
        </div>
    )
}
  • src 디렉토리에 App.scss 파일을 생성하고 작성
.box {
    display: inline-block;
    width: 100px;
    height: 100px;
    border: 1px solid black; 
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    &.blue{
        background: blue;
    }

    &:hover {
        background: yellow;
    }
    
    &:active { 
        background: red;
    }

    .box-inside { 
        background: black; 
        width: 50px; 
        height: 50px;
    }
  }
  • src 디렉토리에 App.js 파일 수정
import React, {Component} from 'react'
import styles from './App.scss';
import classNames from 'classnames/bind';

const cx = classNames.bind(styles)

class App extends Component{
  render(){
    const isBlue = true;
    return(
      <div className={cx('box', {blue:isBlue})}>
        <div className={cx('box-inside')} />
      </div>
    )
  }
}

export default App;

  • src 디렉토리에 styles 디렉토리 생성
  • sytles 디렉토리에 util.scss 파일을 생성하고 변수 와 믹스인 선언
$size: 100px;

@mixin place-at-center(){
    top:50%;
    left:50%;
    transform: translate(-50%, -50%);
}
  • App.scss 파일 수정
@import './styles/util.scss';

.box {
    display: inline-block;
    width: $size;
    height: $size;
    border: 1px solid black; 
    position: fixed;
    
    @include place-at-center();

    &.blue{
        background: blue;
    }
    &:hover {
        background: yellow;
    }
    
    &:active { 
        background: red;
    }

    .box-inside { 
        background: black; 
        width: 50px; 
        height: 50px;
    }
  }


scss 라이브러리

  • 종류

    • 반응형 웹 디자인 (디바이스나 뷰의 크기에 상관없이 동일한 콘텐츠를 사용할 수 있도록 디자인) 을 도와주는 라이브러리 : include-media

    • 색상 설정을 쉽게 해주는 라이브러리 : open-color

$ npm install include-media open-color sass
  • 다른 곳에서 사용할 css 스타일 변수를 소유할 util.scss 파일을 수정
@import '~open-color/open-color';
@import '~include-media/dist/include-media';

$breakpoints: ( 
    small: 376px, 
    medium: 768px, 
    large: 1024px, 
    huge: 1200px
);

$size: 100px;

@mixin place-at-center(){
    top:50%;
    left:50%;
    transform: translate(-50%, -50%);
}
  • components 디렉토리에 Button 디렉토리를 생성
  • 컴포넌트를 생성하기 위한 Button.jsx 파일을 Button 디렉토리에 생성하고 작성
import styles from './Button.scss'; 
import classNames from 'classnames/bind';

const cx = classNames.bind(styles);
const Button = ({children, ...rest}) => { 
    return (
        <div className={cx('button')} {...rest}> 
            {children}
        </div>
    ); 
};

export default Button;
  • 스타일을 작성하기 위한 Button.scss 파일을 Button 디렉토리에 생성하고 작성
@import '/Users/euijoolee/Desktop/React/testapp1/src/styles/Util.scss';

.button {
    background: $oc-green-7; 
    transition: all .2s ease-in; 
    display: inline-block; 
    padding-top: 2rem; 
    padding-bottom: 2rem; 
    text-align: center;
    color: white;
    position: fixed;
    font-size: 2rem; 
    font-weight: 500; 
    border-radius: 4px;
    cursor: pointer;

    @include place-at-center(); 
    
    width: 1200px;
// 반응형 디자인
    @include media("<huge") { 
        width: 1024px;
    }

    @include media("<large") { 
        width: 768px;
    }

    @include media("<medium") { 
        width: 90%;
    }

    // 마우스 상태에 따라 다른 효과 지정
    &:hover {
        background: $oc-green-6;
    }
    &:active { 
        margin-top: 3px;
        background: $oc-green-8; 
    }
}
  • 컴포넌트를 내보기 위한 index.js 파일을 Button 디렉토리에 생성하고 작성
import Button from './Button';
export default Button;
  • App.js 파일 수정
import React, {Component} from 'react'
import Button from '/Users/euijoolee/Desktop/React/testapp1/src/button'


class App extends Component{
  render(){
    return(
     <div>
      <Button>버튼</Button>
     </div>
    )
  }
}

export default App;


profile
무럭무럭 자라볼까

0개의 댓글