REST(Representational Safe Transfer)

  • 데이터를 주고 받는 방식의 아키텍처 (디자인 패턴)

HTTP URI를 통한 자원의 명시

  • url?category=novel 와 같은 쿼리 스트링 방식이 아니라 url/category/novel 과 같은 방식 사용
  • 대문자 사용 지양
  • _ 대신 - 사용
  • URL 마지막에 / 사용하지 않음
  • 행위를 포함하지 않음(insert, update, delete)
  • 가급적 명사 사용(동사 사용 지양)

HTTP Method(Get, Post, Put, Delete)로 해당 자원을 제어하는 행위를 표현

  • GET - Read(Select) : URL/1
  • POST - Create(Insert) : URL
  • PUT - Update : URL/1
  • DELETE - Delete : URL/1

Restful 서비스(API)

  • REST 아키텍처를 준수하는 서비스
  • RESTful 서비스에서는 Controller에서 요청 받은 내용을 처리하고 데이터를 가공해서 처리 결과를 "특정 플랫폼"에 적합한 형태의 View로 만들어서 응답하지 않고 데이터만 처리하거나 응답 데이터가 있다면 JSON/XML로 응답한다. (View와는 무관하다)
  • 클라이언트는 규칙에 맞게 작업을 요청하면 되고 서버는 어떤 플랫폼에서 어떻게 사용할 것인지를 신경쓰지 않고 요청 받은 데이터만 처리하고 응답하면 된다. 클라이언트는 받은 데이터를 알아서 가공해서 사용한다. 즉, 멀티 플랫폼에서 사용 가능하다.
  • 이러한 특성으로, 사용자 플랫폼과 view에 상관하지 않고 요청한 데이터만 응답해주는 오픈 API에서 Restful한 서비스 방식을 많이 사용한다.
  • 어플리케이션의 복잡도 증가로 인해 서비스별 독립적 구축과 운영이 필요해지면서 많이 활용되고 있다.

POSTMAN

  • API를 개발, 테스트, 공유 및 문서화하는 데 사용되는 API 클라이언트
  • 엔드 포인트 URL을 입력하는 테스트에 사용되며 서버로 요청을 보내고 서버에서 응답을 받아 api가 잘 동작하는지 확인할 수 있다.
  • 쿼리스트링 작업이 편리해질 뿐만 아니라 브라우저를 통해서 설정하지 못하는 Header, Body 값 설정 및 수정을 작업할 수 있다.

POSTMAN 설치

POSTMAN 기본 사용법

  • Workspace를 별도로 만들거나 기본 Workspace인 My Workspace를 사용한다.
  • Collection을 통해 request를 그룹화 할 수 있다.
  • Collection 하위 폴더를 지정하여 그룹화 할 수 있다.
  • 폴더 하위에 request를 추가하여 저장해 둘 수 있다.
  • 요청 Method, 요청 URL, 요청 파라미터 등을 알맞게 설정하여 응답을 확인하며 테스트를 진행한다.

 

       <div id="root"></div>
    <script type="text/babel">
        /* state
        state는 컴포넌트 내부에서 바뀔 수 있는 값을 의미한다. 
        props는 부모 컴포넌트가 설정하는 값이지만, state는 컴포넌트 내부에서 설정되는 값이다. 
        props는 읽기 전용으로 사용되지만, state는 변경되는 값을 관리한다.

        클래스형 컴포넌트에서는 state를 직접 사용할 수 있다. 
        함수형 컴포넌트에서는 useState란 hooks로 별도로 기능을 제공하고 있다. 
        
        */

        class Counter extends React.Component {

            /* 컴포넌트를 생성할 때 가장 먼저 호출 되는 생성자 함수이다.  */
            constructor(props) {
                /* 부모 컴포넌트로부터 전달 받은 props를 부모 클래스의 생성자에 전달하면서 컴포넌트를 생성한다. */
                super(props);

                /* state를 초기화 한다. state는 생성자 안에서 초기화 하는 경우 this. 를 붙여야 한다. 
                또한 이름을 state로 작성해야 하며, state에 저장되는 값의 형태는 반드시 Object 리터럴 형태로 작성된다.*/
                this.state = {
                    number : 0
                };
            }
            render() {
                /* state에 저장된 number 상태 값을 구조분해할당을 이용해서 저장 */
                const { number } = this.state; //구조분해할당 
                
                /* state의 값을 변경하기 위해서는 setState 함수를 이용해야 한다. 
                새로운 상태 값을 가지고 있는 Object를 동일한 키로 설정하여 인자를 전달하면 state 객체를 덮어 쓰게 된다. 
                기존 state에 여러 값들이 있다고 해도 동일한 키를 가진 값만 덮어 쓰게 된다.  */
                return(
                    <>
                        <h1 style={ number < 0 ? { color : 'red' } : { color : 'blue' }}>Count : { number }</h1>
                        <button onClick={ () => this.setState({ number : number - 1 }) }>-1</button>
                        <button onClick={ () => this.setState({ number : number + 1 }) }>+1</button>
                        
                    </>
                )
            }
        }

        ReactDOM.createRoot(document.getElementById('root')).render(<Counter/>);
    </script>
       <div id="root"></div>
    <script type="text/babel">
        /* prevState를 활용한 setState 함수 사용
        this.setState를 사용할 시 상태가 변경되고 리랜더링이 되어야 state 값 변경이 적용된다. 
        하나의 이벤트 핸들러에서 여러 번 setState를 호출한다고 누적해서 갱신되지 않는다. 
        이 때 state 객체 대신 함수를 인자로 전달하여 첫번째 인자 prevState,
        두 번째 인자 props를 활용할 수 있다. 만약 props가 필요하지 않은 상황이면 생략 가능하다.
        
        */

        class Counter extends React.Component {

            /* 생성자 함수 없이 클래스의 속성으로 state를 추가하는 것도 가능하다.  
            이 때 this. 는 사용이 불가능하다. this. 은 인스턴스 생성 후 레퍼런스를
            보관할 변수이기 때문에 생성자 혹은 함수 내에서만 사용이 가능하다. */
            state = {
                number : 0
            }
        
            render() {
                /* state에 저장된 number 상태 값을 구조분해할당을 이용해서 저장 */
                const { number } = this.state; //구조분해할당 
                
                /* state의 값을 변경하기 위해서는 setState 함수를 이용해야 한다. 
                새로운 상태 값을 가지고 있는 Object를 동일한 키로 설정하여 인자를 전달하면 state 객체를 덮어 쓰게 된다. 
                기존 state에 여러 값들이 있다고 해도 동일한 키를 가진 값만 덮어 쓰게 된다.  */
                return(
                    <>
                        <h1 style={ number < 0 ? { color : 'red' } : { color : 'blue' }}>Count : { number }</h1>
                        <button onClick={ () => this.setState({ number : number - 1 }) }>-1</button>
                        <button onClick={ 
                            () => {
                                /* setState로 상태를 변경하지만 반영이 되어 있지는 않다. 
                                메모리에만 임시로 변경된 상태를 가지고 있으며
                                해당 함수가 종료되면 변경 내역을 반영한다. */
                                // console.log(`number : ${this.state.number}`); // 그 이전의 상태를 갖고 있고, 함수가 종료될 때 한번에 변경된다.
                                // this.setState({ number : number + 1 });
                                // console.log(`number : ${this.state.number}`);
                                // this.setState({ number : number + 1 });
                                // console.log(`number : ${this.state.number}`);

                                /* 이러한 상황을 해결하기 위해서는 prevState를 이용한다. */
                                this.setState((prevState, props) => {
                                    return {
                                        number : prevState.number + 1
                                    };
                                });

                                this.setState(prevState => ({ number : prevState.number + }));
                            } 
                        }
                        >+1</button>
                        
                    </>
                );
            }
        }

        ReactDOM.createRoot(document.getElementById('root')).render(<Counter/>);
    </script>
    <div id="root"></div>
    <script type="text/babel">
        /* setState 이후 특정 작업 실행하기 */

        class Light extends React.Component {

            state = {
                isOn : false
            };

            render() {

                const { isOn } = this.state;

                const style = {
                    width : 200,
                    height : 200,
                    backgroundColor : isOn ? 'green' : 'red',
                    transition : '2s'

                };

                return (
                    <>
                        <div style={style}></div>
                        <button onClick={
                            () => this.setState(
                                { isOn : !isOn }, //on이면 off, off였으면 on
                                () => console.log(isOn ? '불이 켜졌습니다' : '불이 꺼졌습니다')
                                ) 
                        }
                        >
                            { isOn? 'OFF' : 'ON'}
                        </button>
                    </>
                );
            }
        }

        ReactDOM.createRoot(document.getElementById('root')).render(<Light/>);
    </script>

 

'LECTURE > React' 카테고리의 다른 글

props  (0) 2023.04.17
React rendering  (0) 2023.04.17
[React] JSX intro, 표현법, Fragment, attribute, 주석 작성법  (0) 2023.04.17
[React] intro - createElement and render, history of component  (0) 2023.04.17

props

        properties의 약어로, 컴포넌트의 속성을 설정할 때 사용하는 요소이다. 
        props 값은 해당 컴포넌트를 사용하는 부모 컴포넌트에서 설정할 수 있다. 
        props는 읽기 전용이기 때문에 수정해서는 안된다.

<body>
    <div id="root"></div>
    <script type="text/babel">

        function Title(props) {
            return <h1> 안녕하세요 { props.name }님, 환영합니다.</h1>;
        }

        /* props.name이 존재하지 않을 경우의 기본 값 설정 */
        Title.defaultProps = {
            name : '아무개'
        }

        const name1 = "홍길동";
        const name2 = "유관순";

        ReactDOM.createRoot(document.getElementById('root')).render(
        [   
            <Title name={ name1 }/>,
            <Title name={ name2 }/>,
            <Title name=" 이순신 "/>,
            <Title/>
        
        ] 
        );

    </script>

 

children-props

<body>
    <div id="root"></div>
    <script type="text/babel">

        function ChilerenNodePrinter(props) {

            console.log(props);

            return(
                <>
                    <h1>자식 노드가 가지고 있는 값은?</h1>
                    <h3>children : <font style={ {color : 'orange'} }>{ props.children }</font></h3>
                </>
            );
        }

        ReactDOM.createRoot(document.getElementById('root')).render(
           [
                <ChilerenNodePrinter name="홍길동" phone="010-1234-5678">텍스트노드</ChilerenNodePrinter>,
                <ChilerenNodePrinter><div>자식노드</div></ChilerenNodePrinter>,
                <ChilerenNodePrinter><div>1</div><div>2</div><div>3</div></ChilerenNodePrinter>
           ] 
        );

    </script>

    
</body>

 

구조분해할당

S6에서 제공하는 구조분해할당 문법을 이용하여 props 객체 내부의 값을 바로 추출해서 사용할 수 있다.

(props. 를 생략하고 사용 가능하다)

1. 전달 받은 props 인자를 구조분해할당 하는 방법

         function PropsPrinter(props) {

             const { name, children } = props;

             return (
                 <>
                     <h1>제 이름은 { name }입니다. </h1>
                     <h3>제가 가지고 있는 children은 { children }입니다. </h3>
                 </>
             )
         }
         
         ReactDOM.createRoot(document.getElementById('root')).render(
        <PropsPrinter name ="홍길동">텍스트노드</PropsPrinter>
       );

 

2. 전달 받은 인자를 구조분해할당 하는 방법

           function PropsPrinter({ name, children}) {

                return (
                    <>
                        <h1>제 이름은 {name}입니다. </h1>
                        <h3>제가 가지고 있는 children은 {children}입니다. </h3>
                    </>
                )
            }

       ReactDOM.createRoot(document.getElementById('root')).render(
        <PropsPrinter name ="홍길동">텍스트노드</PropsPrinter>
       );

 

props-type-verify


        props의 타입이 잘못 되었다거나 필수 타입이 처리되지 않았음을 콘솔을 통해 알려준다. (랜더링이 되기는 함)

        필수적인 설정은 아니나 큰 규모의 프로젝트를 진행하거나 협업하게 되면 해당 컴포넌트에 어떤 props가 필요한지 
        쉽게 알 수 있어 개발 능률이 좋아질 수 있다. 

<body>
    <div id="root"></div>

    <script type="text/babel">

        function PropsVerify({ name, favoriteNumber, children }) {

            return (
                <>
                    <h1>제 이름은 { name } 입니다.</h1>
                    <h2>제가 좋아하는 숫자는 { favoriteNumber } 입니다.</h2>
                    <h3>제가 가지고 있는 children은 { children } 입니다.</h3>
                </>
            );
        }

        PropsVerify.propTypes = {
            name : PropTypes.string, 
            favoriteNumber : PropTypes.number.isRequired
        };

        ReactDOM.createRoot(document.getElementById('root')).render(
            [
                <PropsVerify name="홍길동" favoriteNumber={ 5 }>텍스트노드</PropsVerify>,
                <PropsVerify name={ 3 }>텍스트노드</PropsVerify> 
                // name숫자 전달, number 전달 안 함
            ]
        );

    </script>

</body>

 

'LECTURE > React' 카테고리의 다른 글

state  (0) 2023.04.18
React rendering  (0) 2023.04.17
[React] JSX intro, 표현법, Fragment, attribute, 주석 작성법  (0) 2023.04.17
[React] intro - createElement and render, history of component  (0) 2023.04.17

리액트 랜더링 

 아래 주석 안에 들어가는 모든 엘리먼트를 ReactDOM에서 관리하기 때문에 이것을 Root DOM node라고 부르다. 일반적으로 React로 구현된 애플리케이션은 하나의 루트 돔 노드가 있다.

  단, 리액트를 기존 앱에서 통합하려는 경우에는 원하는 만큼의 독립된 루트 돔 노드를 가질 수도 있다.

React Element를 루트 DOM 노드에 렌더링 하려면
        ReactDOM.createRoot(루트 DOM 노드).render(렌더링 할 엘리먼트)로 처리하면 된다.

<body>

    <div id="root"></div> <!-- 루트 돔 노드-->
    
    <script type="text/babel">

        const element = (
            <>
                <h1>What time is it now?</h1>
                <h3>It is { new Date().toLocaleTimeString() }</h3>
            </>
        );

        /* React Element를 루트 DOM 노드에 렌더링 하려면
        ReactDOM.createRoot(루트 DOM 노드).render(렌더링 할 엘리먼트)로 처리하면 된다. */
        ReactDOM.createRoot(document.getElementById('root')).render(element);

    </script>
</body>

 

리액트 엘리먼트는 불변 객체(immutable)이다.
        엘리먼트를 생성한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없다.
        따라서 엘리먼트를 업데이트 하기 위해서는 완전히 새로운 엘리먼트를 만들고 다시 render를 호출해야 한다.

<body>

    <div id="root"></div> <!-- 루트 돔 노드-->
    
    <script type="text/babel">

        const ReactClientDOM = ReactDOM.createRoot(document.getElementById('root')); //한번만 일어나도록

        function tick() { //엘리먼트를 function 안에 작성, 이 함수를 1초에 한 번씩 부른다
            const element = (
            <>
                <h1>What time is it now?</h1>
                <h3>It is { new Date().toLocaleTimeString() }</h3>
            </>
             );

          ReactClientDOM.render(element); // element를 렌더링하는 동작은 초마다 일어나도록
       
        }

        tick(); //생성
        setInterval(tick, 1000); //1초에 한번씩 불러온다. 


    </script>


        1초 마다 tick 함수를 호출하여 새로운 엘리먼트를 생성하고 랜더링 한다.
        매 초마다 새로운 엘리먼트가 다시 랜더링 되지만 개발자 도구의 elements를 살펴보면
        실제 업데이트 되는 부분만 갱신 되는 것을 확인할 수 있다. 
        
        리액트 엘리먼트는 메모리 상에서 연산되는 가상의 돔으로, 실제 돔과 동일한 렌더 트리를 가지고 있다. (복제본)
        render 호출 시 새로운 렌더 트리를 만들고 가상의 돔과 비교 (diff 알고리즘) 후, 변화가 있는 부분만 실제 돔에 반영

          -> 돔에 대한 재구성을 많이 하지 않기 때문에 효율적이다

 

조건부 랜더링

  여러 개의 엘리먼트 중 특정 조건에 따라 하나의 엘리먼트만 랜더링 할 수 있다.  

<body>
    <div id="root"></div> 
    
    <script type="text/babel">
    
    const answer = parseInt(prompt('리액트가 재미 있으신가요? \n1. 재미있다. \n2. 어렵다.'));

    1. 랜더링 시 조건 비교 후 조건부 랜더링 

    const positiveElement = <h1>앞으로 점점 더 재밌어 질 거 예요^^</h1>
    const negativeElement = <h1>천천히 앞의 내용을 복습해보세요^^</h1>

    ReactDOM.createRoot(document.getElementById('root'))
        .render((answer==1) ? positiveElement : negativeElement);



     2. JSX 내에서 조건부로 엘리먼트 생성 (보편적)
     2-1. if문 

     let element;
     if(element === 1) {
         element = <h1>앞으로 점점 더 재밌어 질 거 예요^^</h1>;
     } else {
         element = <h1>천천히 앞의 내용을 복습해보세요^^</h1>;
    }
    
    ReactDOM.createRoot(document.getElementById('root')).render(element);



     2-2. 삼항연산자 (if 보다 많이 쓰인다)

     const element = (answer === 1) ? (
         <h1>앞으로 점점 더 재밌어 질 거 예요^^</h1>
     ) : (
         <h1>천천히 앞의 내용을 복습해보세요^^</h1>
     );
     ReactDOM.createRoot(document.getElementById('root')).render(element);



     2-3. && 연산자를 이용한 조건부 엘리먼트 생성
     특정 조건을 만족하는 경우에만 랜더링을 하고, 그렇지 않은 경우엔 아무것도 보이지 않게 하고자 할 때 사용한다. 

const element = answer === 1 && <h1>앞으로 점점 더 재밌어 질 거 예요^^</h1>;

 ReactDOM.createRoot(document.getElementById('root')).render(element);


     주의 사항
    false 조건을 가지고 랜더링을 하는 경우 조건에 일치하지 않으면 랜더링 되는 요소가 없지만

    0과 같이 falsy한 값을 이용해 조건부 엘리먼트 생성을 하면 판단은 false지만 0을 반환한다.  


    const number = 0;
    const element = number && <h1>0이 아닙니다</h1>;
    
    ReactDOM.createRoot(document.getElementById('root')).render(element);
    </script>
    
    
</body>

 

 

'LECTURE > React' 카테고리의 다른 글

state  (0) 2023.04.18
props  (0) 2023.04.17
[React] JSX intro, 표현법, Fragment, attribute, 주석 작성법  (0) 2023.04.17
[React] intro - createElement and render, history of component  (0) 2023.04.17

JSX


        createElement를 이용해 엘리먼트를 정의하면 복잡하고 가독성이 떨어진다.
        리액트 팀은 ReactElement를 정의하는 간단한 방법으로 JSX 문법을 제공하는데
        자바스크립트를 확장한 문법으로 ReactElement를 XML 문법 형식으로 정의할 수 있는 방법이다.

                                                                                     <key>value</key>

         단, JSX는 공식적인 자바스크립트 문법이 아니어서 바벨이라는 트랜스 컴파일링 툴이 필요하다.
        바벨은 ES6 + ES6 next 문법을 ES5 문법으로 변환해주는 도구인데
        JSX를 순수 리액트로 변환해주는 기능도 포함하고 있다. (바벨이 JSX 처리 표준이다.)

 

1. 바벨 CDN 추가

https://unpkg.com/ : cdn 경로 얻기 위한 사이트

unpkg.com/:package@:version/:file  : 다운 받는 경로 작성법

<script src='https://unpkg.com/@babel/standalone/babel.min.js'></script> : 바벨 CDN 추가

<!DOCTYPE html>
<html lang="kor">
<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>01_JSX-intro </title>
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <script src='https://unpkg.com/@babel/standalone/babel.min.js'></script>
</head>

 

2. script 태그의 type 속성 추가 : <script type = "text/babel">

<body>

    <div id="root"></div>

    <script type = "text/babel">
        const helloWord = <h1>Hello World!</h1>;

        ReactDOM.createRoot(document.getElementById('root')).render(helloWord);

    </script>
    
</body>
</html>

JSX문법은 html 문법과 비슷하지만 차이점이 있다.

 

JSX 표현법

JSX 문법 내에 중괄호를 이용하여 모든 javascript 표현식(문법)을 넣을 수 있다.

<body>
    <div id="root"></div>

    <script type="text/babel">

        function formatName(user) { //user라는 객체를 받아서 lastName과 firstName을 반환한다.

            return `${user.lastName} ${user.firstName}`;
        }

        const user = {
            firstName : 'Gilldong',
            lastName : 'Hong'
        };

        /* JSX 문법 내에 중괄호를 이용하여 모든 javascript 표현식(문법)을 넣을 수 있다. */
        const element = <h1>Hello, { formatName(user) }</h1>

        ReactDOM.createRoot(document.getElementById('root')).render(element);

    </script>
    
</body>

 

Fragment

<body>
    <div id="root"></div>
    
    <script type="text/babel">
        
        const user = {
            name : 'Gildong',
            phone : '010-1234-5678'
        };

		- 아래 내용 -
	
        ReactDOM.createRoot(document.getElementById('root')).render(element);
    </script>
</body>

       

 1.최상위 엘리먼트가 두 개 이상이면 에러가 발생한다 -> 하나의 돔 트리만 생성해야 한다. 형제 노드 X

        const element = (
            <h1>Hello, { user.name }</h1>
            <h3>phone : { user.phone}</h3>
        ); -> 에러 발생



      2. <div> 태그로 감싸서 하나의 돔 트리를 생성할 수 있도록 한다. -> 불필요한 div 태그가 생성된다.

        const element = (
           <div>
                <h1>Hello, { user.name }</h1>
                <h3>phone : { user.phone}</h3>
            </div>
        );



   3. <React.Fragment>로 감싸서 형식상의 돔 트리상 상위 엘리먼트를 만들어 준다.

             const element = (
             <React.Fragment>
                 <h1>Hello, { user.name }</h1>
                 <h3>phone : { user.phone}</h3>
             </React.Fragment>
         );



    4. React 라이브러리로부터 Fragment 객체만 구조분해할당 해주면 React. 생략 가능하다. -> 미리 정의해서 사용

     const { Fragment } = React; // React의 기능 중에 Fragment를 꺼내서 사용할거야

     const element = (
             <Fragment>
                 <h1>Hello, { user.name }</h1>
                 <h3>phone : { user.phone}</h3>
             </Fragment>
         );



    5. React.Fragment의 축약 문법인 <></>로 감싸서 사용할 수 있다 (babel 17 부터 제공)

        const element = (
            <>
                <h1>Hello, { user.name }</h1>
                <h3>phone : { user.phone}</h3>
            </>
        );

 

attribute

JSX는 HTML 태그처럼 보이지만 실제로는 자바스크립트이다. (React.createElement를 호출하는 코드로 컴파일 된다.)
 따라서 자바스크립트의 예약어와 동일한 이름의 attribute는 다른 이름으로 우회적으로 사용하도록 정의되어 있다.

<head>
<style>
        .hightlight {
            background-color: red;
        }
    </style>

</head>
<body>
    
    <div id="root"></div>
    
    <script type="text/babel">

    
     1. 문자열로 속성 값 정의하는 방법 

    const element = <h1 id="title" className="hightlight">Hello World!</h1> 
    //id는 id, class는 className



     2. 자바스크립트 표현식을 이용하는 방법

    const id = "title"
    const className = "hightlight"
    const element = <h1 id={ id } className={ className }>Hello World!</h1>;

    ReactDOM.createRoot(document.getElementById('root')).render(element);
    
    </script>

</body>



inline style attribute

        style 속성에 객체 형태의 attribute를 추가해야 한다.
         객체의 속성명은 카멜 케이스로 작성한다. 
         속성 값은 문자열 혹은 숫자 형태로 작성해야한다.
   

<body>

    <div id="root"></div>

    <script type="text/babel">

        const style = {
            backgroundColor : 'black',  // 속성명(key) 카멜케이스, 속성값(value) 숫자 or 문자열
            color : 'white',
            cursor : 'pointer',
            textAlign : 'center',
            padding : 20
            // 단위를 작성하려면 문자열로 사용하지만('20px') 단위를 생략하면 숫자만 사용 가능
        };

        const element = (
            <>
                <h1 style={ style }>Hello World!</h1> //객체 형태의 어트리뷰트 추가
                <h3>inline styling test</h3>
            </>
        );

        ReactDOM.createRoot(document.getElementById('root')).render(element);

    </script>
    
</body>

 

event-attribute

<body>
    <div id="root"></div>
    
    <script type="text/babel">


        1. 인라인으로 간단한 이벤트 적용 시 JSX의 자바스크립트 표현식 내에  이벤트 처리 함수 작성 가능 
        

        const element = (
             <>
                 <h1>Event Attribute</h1> 
                 <button onClick={ () => alert('Hello World!') }>클릭하세요</button>
             </>
        );

-> html의 인라인 요소가 아니라 리액트로 처리하여 자바스크립트 코드로 컴파일 된다. 


        2. 이벤트 동작 시 사용할 핸들러 메소드 사전 정의 후 JSX 내에 표현식으로 사용 


        const onClickHandler = () => {
            alert('Hello World!');
        };

        const element = (
            <>
                <h1>Event Attribute</h1> 
                <button onClick={ onClickHandler }>클릭하세요</button>
            </>
        );

        ReactDOM.createRoot(document.getElementById('root')).render(element);
    </script>

</body>

-> 정의된 함수 onClickHandler를 onClick 에 전달한다.

 

JSX 주석 사용법

{/* 주석  작성*/}

개행 // 주석 작성

<body>
    <div id="root"></div>
    
    <script type="text/babel">

        const element = (
            <>
                <h1>Comment in JSX</h1>
                {/* JSX 내에서의 주석은 이렇게 작성합니다. */}    
                <h3
                    id="text"           //시작 태그를 여러 줄로 작성한다면 여기에도 주석 작성 가능 
                    className="text"
                >
                    JSX 내의 주석 사용하는 방법
                </h3>
                /*하지만 이런 주석이나*/
                //이런 주석은 페이지에 그대로 나타내게 됩니다.
            </>
        );
         
         ReactDOM.createRoot(document.getElementById('root')).render(element); //랜더링
   
   </script>

    
</body>

 

 

'LECTURE > React' 카테고리의 다른 글

state  (0) 2023.04.18
props  (0) 2023.04.17
React rendering  (0) 2023.04.17
[React] intro - createElement and render, history of component  (0) 2023.04.17

DOM

메모리 상에 존재하는 트리 형태의 객체 

노드(node)

요소 노드(테그) - 어트리뷰트 노드 - 텍스트 노드

 

React

JavaScript 라이브러리. 가장 큰 강점은 가상 돔을 사용하여 실제 DOM 업데이트의 성능 비용을 줄인다.

<!DOCTYPE html>
<html lang="kor">
<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>01_createElement-and-render</title>
    <!-- 리액트 공식 사이트의 문서에 웹 사이트 React 추가하기 메뉴의 cdn을 복사해서 넣어준다.-->
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
</head>

 

React.createElement를 사용해 h1 태그를 표현하는 엘리먼트를 만들 수 있다.
        첫 번째 인자 : 엘리먼트 타입을 문자열로 정의
        두 번째 인자 : 엘리먼트 프로퍼티 표현
        세 번째 인자 : 엘리먼트 자식 노드 표현 
        
        랜더린 과정에서 리액트는 이 엘리먼트를 실제 DOM 엘리먼트로 변환한다.
        <h1 id="greeting">hello world!</h1>

        리액트 엘리먼트는 단지 리액트에게 DOM 엘리먼트를 구성하는 방법을 알려주는 자바스크립트의 객체로
        실제 DOM 객체가 아니다. -> 따라서 DOM API랑 혼용하면 문제가 생긴다.

const greetingElement = React.createElement('h1', { id : 'greeting'}, 'hello world!'); //h1 태그, id 속성, 내부 텍스트
const textElement = React.createElement('h3', { id : 'name' }, '안녕하세요');

 

리액트 엘리먼트와 그 모든 자식 엘리먼트를 함께 랜더링하기 위한 함수
        첫 번째 인자 : 리액트 엘리먼트
        두 번째 인자 : 랜더링이 일어날 대상 DOM 노드 

ReactDOM.render(
             greetingElement,
             document.getElementById('root')  // 실제 삽입할 돔 노드
         );

 

16버전 이전의 엘리먼트는 한 엘리먼트만 랜더링 가능했지만, 이후 버전에서는 배열을 랜더링 할 수도 있다.

ReactDOM.render(
            [greetingElement, textElement],
             document.getElementById('root')
         );

 

18버전

ReactDOM.createRoot(document.getElementById('root')).render([greetingElement, textElement]);

 

컴포넌트

재사용이 가능한 단위

클래스 문법

ES2015(ES6)에 클래스 문법이 도입되면서 리액트에서도 리액트 컴포넌트를 클래스로 만드는 방법이 도입되었다.

    class helloWord extends React.Component {
        render() {
            return React.createElement(
                'h1',
                { className : 'greeting' },
                'Hello World!'
            );  
        }
    

    }
    ReactDOM.createRoot(document.getElementById('root')).render(
        React.createElement(helloWord)
    );

. React.Component를 상속하여 재사용이 가능한 클래스 형태의 컴포넌트이다.

render라는 메소드를 정의하면서 실제 랜더링하고 싶은 리액트의 엘리먼트를 최종적으로 반환한다.

반환된 값이 랜더링될 수 있도록 돔의 노드를 설정해서 만들고, 리액트의 createElement를 이용해서 반환된 값을 랜더링한다.

 

함수 기반 문법

현재도 class 구문을 사용해 리액트 컴포넌트를 만들 수 있지만 리액트 16버전부터 함수 기반의 컴포넌트를 생성하는 방법이 추가 되었고 현재 주를 이루고 있다.

    function HelloWorld() {

        return React.createElement('h1', { className : 'greeting' }, 'Hello World!');
    }

    ReactDOM.createRoot(document.getElementById('root')).render(
        React.createElement(HelloWorld)
    );

 

 

 

'LECTURE > React' 카테고리의 다른 글

state  (0) 2023.04.18
props  (0) 2023.04.17
React rendering  (0) 2023.04.17
[React] JSX intro, 표현법, Fragment, attribute, 주석 작성법  (0) 2023.04.17

Spring data JPA

1. 스프링 데이터 JPA(Spring Data JPA) 개요

1-1. 스프링 데이터 JPA란?

  • Spring 프레임워크에서 JPA를 편리하게 사용할 수 있도록 제공하는 Spring Data 모듈 중 하나이다.
  • JPA를 추상화시킨 Repository라는 인터페이스를 사용하고 쿼리메소드(Query Method)를 호출하는 것만으로 손쉽게 SQL문을 생성할 수 있다. find, persist, remove

1-2. 스프링 데이터 JPA의 특징

  • Spring 및 JPA기반의 Repository 구축을 위해 인터페이스와 쿼리메소드를 제공한다.
  • Querydsl 쿼리 지원 및 이에 따른 안전한 JPA 쿼리를 처리해 준다.
  • pagination을 지원한다.
  • Spring Data Jpa를 사용하면 JPA에서 사용했던 기존의 EntityManagerFactory, EntityManager, EntityTransaction같은 객체가 필요 없다.

1-3. Repository 인터페이스

1-3-1. Spring Data JPA의 Repository 인터페이스 상속구조

  • Repository 인터페이스 상속 구조

 

  • 인터페이스 종류 및 설명인터페이스 설명
    Repository 따로 기능은 없다.
    CrudRepository 주로 CRUD 기능을 제공한다..
    PagingAndSortingRepository 검색 및 검색 결과를 페이징 처리하고자 할 경우 사용한다.
    JpaRepository 영속성 컨텍스트 플러시 및 배치에서 레코드 삭제와 같은 일부 JPA 관련 추가 방법들을 제공한다. 최종적으로 상속함

1-3-2. Repository 인터페이스의 메소드

  • 주로 CrudRepository 인터페이스가 제공하는 메소드를 사용하게 된다.
  • 메소드 종류 및 설명제공 메소드 설명 
    long count() 모든 엔티티의 개수 리턴
    void delete(ID) 식별 키를 통한 삭제
    void delete(Iterable) 주어진 모든 엔티티 삭제
    boolean exist(ID) 식별 키를 가진 엔티티가 존재 하는지 확인
    findAllById(ID) 모든 엔티티 목록 리턴
    Iterable findAll(Iterble) 해당 식별 키를 가진 엔티티 목록 리턴
    Optional findById(ID) 해당 식별 키에 해당하는 단일 엔티티 리턴
    saveAll 여러 엔티티들을 한 번에 등록, 수정
    <S extends T>S save<S entity> 하나의 엔티티를 등록, 수정

1-4. 쿼리 메소드(Query Methods)

1-4-1. 쿼리 메소드란?

  • JPQL을 메소드로 대신 처리할 수 있도록 제공하는 기능이다.
  • 메소드의 이름으로 필요한 쿼리를 만들어주는 기능으로 "find + 엔티티 이름 + By + 변수 이름"과 같이 네이밍 룰만 알면 사용 가능하다. ex) findMenuByCode(): Menu 엔티티에서 Code 속성에 대해 조건처리하여 조회한다. -> 엔티티 제네릭 선언하면 생략이 가능하다
  • 엔티티 이름을 생략하고 쓸 수도 있는데 이는 해당 Repository 인터페이스의 제네릭에 해당하는 엔티티를 자동으로 인식하기 때문이다. ex) Repository 인터페이스가 JpaRepository<Menu, Integer>를 상속받고 있다면, findByCode(): Menu 엔티티에서 Code 속성에 대해 조건처리하여 조회한다.

1-4-2. 쿼리 메소드 유형

  • 다양한 쿼리 메소드 형태를 위한 키워드와 예시 및 생성되는 SQL

                키워드                                 쿼리 메소드 형태  예시                                              생성 SQL

And findByCodeAndName where x.code =?1 and x.name = ?2
Or findByCodeOrName where x.code =?1 or x.name = ?2
Between findByPriceBetween where x.price between ?1 and ?2
LessThan findByPriceLessThan where x.price < ?1
LessThanEqual findByPriceLessThanEquals where x.price <= ?1
GreaterThan findByPriceGreaterThan where x.price > ?1
GreaterThanEqual findByPriceGreaterThanEqual where x.price >= ?1
After findByDateAfter where x.date > ?1
Before findByDateBefore where x.date < ?1
IsNull findByNameIsNull where x.name is null
IsNotNull, NotNull findByName(Is)NotNull where x.name is not null
Like findByNameLike where x.name like ?1
NotLike findByNameNotLike where x.name not like ?1
StartingWith findByNameStartingWith where x.name like ?1
EndingWith findByNameEndingWith where x.name like '%'
Containing findByNameContainign where x.name like '%'
OrderBy findByPriceOrderByCodeDesc where x.price = ?1 order by x.code desc
Not findByNameNot where x.name <> ?1
In findByNameIn(Collection names) where x.name in ?1

 

 

'LECTURE > JPA' 카테고리의 다른 글

조인 Join, 서브쿼리 Subquery  (0) 2023.04.11
페이징 Paging, 그룹함수  (0) 2023.04.11
JPA association mapping  (0) 2023.04.10
JPA 개요 및 영속성 컨텍스트  (0) 2023.04.06

조인 JOIN

조인이 종류

1. 일반 조인 : 일반적인 SQL 조인을 의미 (내부 조인, 외부 조인, 컬렉션 조인, 세타 조인 a.k.a. 크로스 조인)

2. 페치 조인 : JPQL에서 성능 최적화를 위해 제공하는 기능으로, 연관된 엔티티나 컬렉션을 한 번에 조회할 수 있다.

지연 로딩이 아닌 즉시 로딩을 수행하며 join fetch 명령어를 사용한다.

 

 

내부 조인

: Menu 엔티티에 대한 조회만 일어나고 Category 엔티티에 대한 조회는 나중에 필요할 때 일어난다. (지연 로딩)

select의 대상은 영속화하여 가져오지만 조인의 대상은 영속화하여 가져오지 않는다.

 

    @Test
    public void 내부조인을_이용한_조회_테스트 () {
    	
    	//when
    	String jpql = "SELECT m FROM section06_menu m JOIN m.category c"; //항상 필드를 기준으로~
    	List<Menu> menuList = entityManager.createQuery(jpql, Menu.class).getResultList();
    	
    	//then
    	assertNotNull(menuList);
    	menuList.forEach(System.out::println); // 조회는 메뉴만, 카테고리는 지연 로딩, 카테고리 개수만큼 조회
    	
    }

 

select category0_.CATEGORY_NAME as col_0_0_,
menulist1_.MENU_NAME as col_1_0_
from TBL_CATEGORY category0_ l
eft outer join TBL_MENU menulist1_
on category0_.CATEGORY_CODE=menulist1_.CATEGORY_CODE

 

외부 조인

    @Test
    public void 외부조인을_이용한_조회_테스트() {
    	
    	//when
    	String jpql = "SELECT m.menuName, c.categoryName FROM section06_menu m RIGHT JOIN m.category c " 
    				+ "ORDER BY m.category.categoryCode"; //category 필드의 categoryCode
    	List<Object[]> menuList = entityManager.createQuery(jpql, Object[].class).getResultList();
    	
    	//then
    	assertNotNull(menuList);
    	menuList.forEach(row -> {
    		Stream.of(row).forEach(col -> System.out.print(col + " ")); 
    		//배열, 컬렉션 Stream으로 변환가능, 값이 없을 경우 null반환
    		System.out.println(); //행 간
    	});
    }
Hibernate:
select menu0_.MENU_NAME as col_0_0_,
category1_.CATEGORY_NAME as col_1_0_
from TBL_MENU menu0_
right outer join TBL_CATEGORY category1_
on menu0_.CATEGORY_CODE=category1_.CATEGORY_CODE order by menu0_.CATEGORY_CODE

 

컬렉션 조인

: 컬렉션 조인은 의미상 분류된 것으로 컬렉션을 지니고 있는 엔티티를 기준으로 조인하는 것을 말한다.

 

    @Test
    public void 컬렉션조인을_이용한_조회_테스트() {

    	//when
    	String jpql = "SELECT c.categoryName, m.menuName FROM section06_category c LEFT JOIN c.menuList m"; 
    	List<Object[]> categoryList = entityManager.createQuery(jpql, Object[].class).getResultList();
    	
    	//then
    	assertNotNull(categoryList);
    	categoryList.forEach(row -> {
    		Stream.of(row).forEach(col -> System.out.print(col + " ")); 
    		System.out.println(); 
    	});
    }
Hibernate:
select category0_.CATEGORY_NAME as col_0_0_,
menulist1_.MENU_NAME as col_1_0_
from TBL_CATEGORY category0_
left outer join TBL_MENU menulist1_
on category0_.CATEGORY_CODE=menulist1_.CATEGORY_CODE

 

세타 조인

: 조인되는 모든 경우의 수를 다 반환하는 크로스 조인과 같다.

    @Test
    public void 세타조인을_이용한_조회_테스트() {
    	
    	//when
    	String jpql = "SELECT c.categoryName, m.menuName FROM section06_category c, section06_menu m"; 
    	List<Object[]> categoryList = entityManager.createQuery(jpql, Object[].class).getResultList();
    	
    	//then
    	assertNotNull(categoryList);
    	categoryList.forEach(row -> {
    		Stream.of(row).forEach(col -> System.out.print(col + " ")); 
    		System.out.println(); 
    	});
    }
Hibernate:
select category0_.CATEGORY_NAME as col_0_0_,
menu1_.MENU_NAME as col_1_0_
from TBL_CATEGORY category0_ cross
join TBL_MENU menu1_

 

페치 조인

: 페치 조인을 하면 처음 SQL 실행 후 로딩할 때 조인 결과를 다 조회한 뒤에 사용하는 방식이기 때문에 쿼리 실행 횟수가 줄어들게 된다. 대부분의 경우 성능이 향상 된다.

    @Test
    public void 페치조인을_이용한_조회_테스트 () {
    	
    	//when
    	String jpql = "SELECT m FROM section06_menu m JOIN FETCH m.category c"; 
    	List<Menu> menuList = entityManager.createQuery(jpql, Menu.class).getResultList();
    	
    	//then
    	assertNotNull(menuList);
    	menuList.forEach(System.out::println);
    	
    }
Hibernate:
select menu0_.MENU_CODE as menu_code1_1_0_,
category1_.CATEGORY_CODE as category_code1_0_1_,
menu0_.CATEGORY_CODE as category_code2_1_0_,
menu0_.MENU_NAME as menu_name3_1_0_,
menu0_.MENU_PRICE as menu_price4_1_0_,
menu0_.ORDERABLE_STATUS as orderable_status5_1_0_,
category1_.CATEGORY_NAME as category_name2_0_1_,
category1_.REF_CATEGORY_CODE as ref_category_code3_0_1_
from TBL_MENU menu0_
inner join TBL_CATEGORY category1_ on menu0_.CATEGORY_CODE=category1_.CATEGORY_CODE

 


서브쿼리 Subquery

: JPQL도 SQL 처럼 서브 쿼리를 지원한다.

하지만 select, from절에서는 사용할 수 없고 where, having절에서만 사용이 가능하다.

@Test
public void 서브쿼리를_이용한_메뉴_조회_테스트() {
		
//given
String categoryNameParameter = "한식";
		
//when
String jpql = "SELECT m FROM section07_menu m WHERE m.categoryCode "
	+ "= (SELECT c.categoryCode FROM section07_category c WHERE c.categoryName = :categoryName)";
List<Menu> menuList = entityManager.createQuery(jpql, Menu.class)
		.setParameter("categoryName", categoryNameParameter)
		.getResultList();
			
//then
assertNotNull(menuList);
menuList.forEach(System.out::println);
		
	}

 

네임드쿼리

  • 동적쿼리 : 현재 우리가 사용하는 방식처럼 EntityManager가 제공하는 메소드를 이용하여 JPQL을 문자열로 런타임 시점에 동적으로 쿼리를 만드는 방식

        (동적으로 만들어질 쿼리를 위한 조건식이나 반복문은 자바 코드를 이용할 수 있다.)

  • 정적쿼리 : 미리 쿼리를 정의하고 변경하지 않고 사용하는 쿼리를 말하며, 미리 정의한 코드는 이름을 부여해서 사용하게 된다.

            -> 이를 NamedQuery라고 한다. 어노테이션 방식과  xml방식 두 가지가 있는데 쿼리가 복잡할수록 xml 방식 선호

 

1. 동적쿼리

(동적쿼리는 주로 검색 필터링에 사용한다.)

@Test
	public void 동적쿼리를_이용한_조회_테스트() {
		
		//given
		String searchName = "한우";
		int searchCategoryCode = 4;
		
		//when
		StringBuilder jpql = new StringBuilder("SELECT m FROM section08_menu m ");
		if(searchName != null && !searchName.isEmpty() && searchCategoryCode > 0) {
			jpql.append("WHERE ");
			jpql.append("m.menuName LIKE '%' || :menuName || '%' ");
			jpql.append("AND ");
			jpql.append("m.categoryCode = :categoryCode ");			
		} else {
			
			if(searchName != null && !searchName.isEmpty()) {
				jpql.append("WHERE ");
				jpql.append("m.menuName LIKE '%' || :menuName || '%' ");
			} else if(searchCategoryCode > 0) {
				jpql.append("WHERE ");
				jpql.append("m.categoryCode = :categoryCode ");			
			}
		}
		
		TypedQuery<Menu> query = entityManager.createQuery(jpql.toString(), Menu.class);
		
		if(searchName != null && !searchName.isEmpty() && searchCategoryCode > 0) {
			query.setParameter("menuName", searchName);
			query.setParameter("categoryCode", searchCategoryCode);
		} else {
			if(searchName != null && !searchName.isEmpty()) {
				query.setParameter("menuName", searchName);
			} else if(searchCategoryCode > 0) {
				query.setParameter("categoryCode", searchCategoryCode);
			}
		}
		
		List<Menu> menuList = query.getResultList();
		
		//then
		assertNotNull(menuList);
		menuList.forEach(System.out::println);
		
	}

 

동적 SQL을 작성하기에 JPQL은 많이 어렵다. 컴파일 에러가 발생하는 것이 아니기 때문에 쿼리를 매번 실행해서 확인해야 하는 불편함이 있다.

Criteria나 queryDSL을 활용하면 보다 편리하게 작성할 수 있으며, 쿼리 작성 시 컴파일 에러로 잘못된 부분을 확인할 수 있어 작성하기도 편하다.

마이바티스를 혼용하거나 마이바티스의 구문 빌더 API를 활용해도 좋다.

 

2. 정적쿼리 - 어노테이션 방식

 (문자열로 쿼리를 작성하기 복잡) 

@Entity(name="section08_menu")
@Table(name="TBL_MENU")
@NamedQueries({
	@NamedQuery(name="section08_menu.selectMenuList", query="SELECT m FROM section08_menu m")
})
public class Menu {

생략

 

▼ createNamedQuery 사용

    @Test
    public void 어노테이션_기반_네임드쿼리를_이용한_조회_테스트() {
    	
    	//when
    	List<Menu> menuList = entityManager.createNamedQuery("section08_menu.selectMenuList", Menu.class).getResultList();
    	
    	//then
    	assertNotNull(menuList);
    	menuList.forEach(System.out::println);
    }

 

3. 정적쿼리 - xml 방식

조금 더 복잡한 형태의 쿼리를 작성해야 하는 경우에는 xml방식을 더 선호한다. 

1.  META-INF 폴더 하위에 menu-query.xml

 

2.persistence.xml 파일에 <mapping-file> 추가

 

3. activation, jaxb-impl 라이브러리 추가

 

    @Test
    public void xml기반_네임드쿼리를_이용한_조회_테스트() {
    	
    	//given
    	int menuCodeParameter = 21;
    	
    	//when
    	Menu foundMenu = entityManager.createNamedQuery("section08_menu.selectMenuNameByCode", Menu.class)
    			.setParameter("menuCode", menuCodeParameter)
    			.getSingleResult();
    	
    	//then
    	assertNotNull(foundMenu);
    	System.out.println("foundMenu = " + foundMenu);
    	
    }

 

'LECTURE > JPA' 카테고리의 다른 글

Spring data JPA  (0) 2023.04.14
페이징 Paging, 그룹함수  (0) 2023.04.11
JPA association mapping  (0) 2023.04.10
JPA 개요 및 영속성 컨텍스트  (0) 2023.04.06

+ Recent posts