배움 __IL/TIL 1기

TIL : 81번째- 230331 [3-4-금]

Mo_bi!e 2023. 3. 31. 18:16

※ Keep in mind

 본 내용은 웹개발과정의 강의 중 내용을 복습을 위해서 메모한 것에 불과한 것입니다. 이러한 연유로 강의내용을 오인한 나머지 오기재 및 불기재가 있을 수 있으니 '참고'만 해주시길 바랍니다. 저의 경우에도 본 내용을 단순히 읽은 것이 결코 저의 것이라고 생각하지 않습니다. 본 내용은 복습을 위한 초기 내지 중간 과정에 불과한 것이고, 이후에 내용을 보충 후 인출 및 설명하기 과정이 있어야 비로소 복습의 단추가 어느 정도 마무리되어간다고 볼 수 있습니다.
 따라서 당초에 본 내용은 비공개였습니다. 그럼에도 불구하고 본 내용을 공개한 점은 함께 공부하는 동료들과 나눔을 바탕으로 배움과 성장의 공진화라는 소기의 목적을 달성에 어느 정도 도움이 될 수 있기 때문이라고 생각합니다.

I. 들어가며

보통 fetch 에 promise는 then절로 주로 써!

일반적으로 두번째 방법인 promise변수에 담는방법은 잘 안써

 

메뉴 가지오면 가져온것을 json으로 바꾸어서 활용할거야

화면에것을 바꾸는 작업을 할때 바꾸는것 자체를 생각을버려 바꾸고자하는것을 두가지 연결해야해

첫번쨰는 어떤변수와 연결할것인가(모델) / 두번째 그 데이터를 어떤 지시자를 쓸것인가

우리는데이터라는것만 활용하면 돼

 

view는 모델로 영향받는애들이야! 반복하고있는 내용을 보면

리스트라는 모델준비할건데 리스트 가진만큼 반복할거야

II. Vue.js

1. Vue.js 으로 화면에 binding 하기

(1) Thymeleaf VS Vue.js

1)

 Thymeleaf 용과 Vus.js로 두개로 따로 만들어보자

<!-- 타임리프 -->
<section th:each="menu : ${list}" class="menu border-bottom border-color-1">
    <form class="">
        <h1 th:text="${menu.name}">알랜드 커피</h1> 
        <div class="menu-img-box">
            <a  th:href="@{detail(id = ${menu.id})}"
                href="detail?id=617">
            <img class="menu-img" 
            th:src ="@{/image/product/{img} (img=${menu.img})}"
            src="/image/product/12.png"></a>
        </div>    
        <div th:text ="${menu.price} +' 원' "
             class="menu-price">4500 원
         </div>
        <div class="menu-option-list">
            <span class="menu-option">
                <input class="menu-option-input" type="checkbox">
                <label>ICED</label>
            </span>
            <span class="menu-option ml-2">
                <input class="menu-option-input" type="checkbox">
                <label>Large</label>
            </span>
        </div>
        <div class="menu-button-list">
            <input class="btn btn-line btn-round btn-size-1 rounded-0-md" type="submit" value="수정">
            <input class="btn btn-fill btn-round rounded-0-md btn-size-1 ml-1" type="submit" value="삭제">
        </div>
    </form>
</section>

<!-- Vuw.js  -->
<section th:each="menu : ${list}" class="menu border-bottom border-color-1">
    <form class="">
        <h1 th:text="${menu.name}">알랜드 커피</h1> 
        <div class="menu-img-box">
            <a  th:href="@{detail(id = ${menu.id})}"
                href="detail?id=617">
            <img class="menu-img" 
            th:src ="@{/image/product/{img} (img=${menu.img})}"
            src="/image/product/12.png"></a>
        </div>    
        <div th:text ="${menu.price} +' 원' "
             class="menu-price">4500 원
         </div>
        <div class="menu-option-list">
            <span class="menu-option">
                <input class="menu-option-input" type="checkbox">
                <label>ICED</label>
            </span>
            <span class="menu-option ml-2">
                <input class="menu-option-input" type="checkbox">
                <label>Large</label>
            </span>
        </div>
        <div class="menu-button-list">
            <input class="btn btn-line btn-round btn-size-1 rounded-0-md" type="submit" value="수정">
            <input class="btn btn-fill btn-round rounded-0-md btn-size-1 ml-1" type="submit" value="삭제">
        </div>
    </form>
</section>

2)

이제 Vue.js에서 model을 준비해 

list data는 2개로 하자

Vue
.createApp({
data(){
return{
    test:"hello",
    //모델을 준비해
    list: [
        {id:1,
        name:"아메리카노",
        price:5000
        },
        {id:2,
        name:"카페라떼",
        price:5500
        },
    ]
};
},

3)

for 를 쓰면 가능해그러고 준비한 모델을 바인딩하는거야

<!-- 타임리프 -->
<section th:each="menu : ${list}" 
        v-for="menu in list" 
        class="menu border-bottom border-color-1" >
    <form class="">
        <h1 th:text="${menu.name}">알랜드 커피</h1> 
        <div class="menu-img-box">

그런데 Thymeleaf와 vue가 중복되면 어떻게 되는가?

 

Vue 모델은 자료가 2개인데 그 2개만큼 반복이 이루어져 다만 타임리프 9개가 나와서 9X2 로 18개가 나와

 

4)

page가 열리자마자 server에서 할건지, front에서 가져올건지 model 이용해서 데이터만드는데 두군데서 만들어

thymeleafserver, Vue.js는 브라우저에서 만들어

 

둘다 MVC로 만들고있는데 MVC는 출력할 것을 바인딩하는것에서 시작이 돼 바인딩하면 서버가 많아야 만들어놓은 페이지가 있어

만약 Vue.js데이터가 0개인데, 서버에서 데이터가 와도 0개라서 출력자체를 안해버려

Client에서 반복은 list의 데이터 수만큼이야

 

5)

server에서 하는일과 client 일 둘다하는데 순서 및 책임이 누가지는지 파악을 해야해

Thymeleafserver에서 일하는 애야 서버에서 일하는 결과물을 쓰는거야

실제 서버에서 온 결과물에 대해서 보면 thymeleaf는 흔적은 없어 그만큼 반복이 돼어서 그래

그런데 클라이언트의 Vue는 여전히 존재해 그래서 model만큼 만들어

 

브라우저에서 읽히는 Vue.js가 서버에서만드는거 다 무시하고 망가뜨린다고 할 수있어 그러면은 초기의 목록을 thymeleaf가 더 어렵게 일할 필요가 점점없어져! thymeleaf 코드를 거두어 낼까 Vue.js를 거두어 낼까 선택해야해

 

< Thymeleaf 를 걷어낸 경우>

<!-- Vuw.js  -->
<section v-for="menu in list" class="menu border-bottom border-color-1">
    <form class="">
        <h1 v-text="menu.name">

        </h1> 
        <div class="menu-img-box">
            <a  href="detail?id=617">
            <img class="menu-img" 
             src="/image/product/12.png">
            </a>
        </div>    
        <div v-text="menu.price"
             class="menu-price">

         </div>
        <div class="menu-option-list">
            <span class="menu-option">
                <input class="menu-option-input" type="checkbox">
                <label>ICED</label>
            </span>
            <span class="menu-option ml-2">
                <input class="menu-option-input" type="checkbox">
                <label>Large</label>
            </span>
        </div>
        <div class="menu-button-list">
            <input class="btn btn-line btn-round btn-size-1 rounded-0-md" type="submit" value="수정">
            <input class="btn btn-fill btn-round rounded-0-md btn-size-1 ml-1" type="submit" value="삭제">
        </div>
    </form>
</section>

이렇게 나와

 

 

6)

이제 Thymeleaf 부분을 지우자!

(2) Vue.js 와 list에 데이터 넣기

1)

psuh()로 세팅한다

categoryClickHandler(e){
    // 	this.load(e.target.dataset.id, function(){
    // 		console.log("데이터 도착?");
    // 	});
    // console.log("click");
    // await this.load(e.target.dataset.id); //기다릴테니까 난 기다렸다가 할거야 -> 동기형으로
    // console.log("데이터가 도착 한 후에 할일");
    //데이터를 요청하고 그 요청이 도착하면 할 일이 여기있는거야
    // 그런데, 도착하기 전에 실행되는... 불상사
        this.list.push()



        this.load(2);
    },

원통 뒤로 넣기는 push(=unshift) / 뒤에서 빼는건 pop(==shift) /앞에서 빼는건 unshitf / 넣는건 shift

 

methods:{
    //이름설정하고 핸들러 받기!
     categoryClickHandler(e){
    // 	this.load(e.target.dataset.id, function(){
    // 		console.log("데이터 도착?");
    // 	});
    // console.log("click");
    // await this.load(e.target.dataset.id); //기다릴테니까 난 기다렸다가 할거야 -> 동기형으로
    // console.log("데이터가 도착 한 후에 할일");
    //데이터를 요청하고 그 요청이 도착하면 할 일이 여기있는거야
    // 그런데, 도착하기 전에 실행되는... 불상사
        this.list.push({id:4, name:"디카 아메", price:5000})



        this.load(2);
    },

실제로 list객체에다가 데이터를 넣는다.

위와 같이 수제청을 누르면 저장한 list가 추가되어서 새로 load가 돼!

그냥 화면을 바꾸려고 하면 화면과 연결되어있는 model로 걔를 조작하면돼

(3) 생명주기 파악

1)

이렇게 해도되긴한데, 데이터를 언제 가져오냐야?

클릭할때가 아니라 페이지 처음에 뜰 때 실행되냐에 실행되는 블럭이 뭔지 알자 그러려면 API문서를 보자!

 

Lifecycle을 보면 다양한 과정이있어

화면이 만들어지면서 다 할수있게 준비되어있어

a. beforeCreate: 는 만들어지기 전이겠지?

b. created : 는 인스턴스가 모든상태를 처리하는거를 마무리하자마자 호출되도록 만들어주는거야

c. mount : 는 unix에서 쓰던 용어야 ! 장치나 노드를 '연결'해서 사용할수 있게 하는거야! 얘를 활용하는 view가 실제화면에서 완전히 binding해서 활용할수있는 상태를 의미한는거야!

dom에 노드가 붙어서 화면에 출력되면 mount! 출력이 안되면 unmounted야!

d. 화면에 보이고 나서 바뀔 때를 update라고 해!(마치 카테고리 눌렀을 때 추가로 생겼을때)

2)

라이프사이클은 옵션과 등위야 그래서 .createApp({}) 안에 위치를 잘 두자!

    }
},

//위 옵션들과 동급이여서 여기에 두는거야!
beforeCreate(){console.log("beforeCreate")},
created(){console.log("created")},
beforeMount(){console.log("beforeMount")},
mounted(){console.log("mounted")},
beforeUpdate(){console.log("beforeUpdate")},
updated(){console.log("updated")},
beforeUnmount(){console.log("beforeUnmount")},
unmounted(){console.log("unmounted")}

})
.mount("#main-section");

이런애들은 하나의 순서사이에 넣는거야 마치 이벤트처리로 볼 수있어

처음에 로드됐을 때

create는 화면보이기 전! mount는 화면보이고 나서

mount가 이렇다는건 즉 화면이 뜨고나서 조작이 가능해

이렇게 특정 버튼을 눌러서 '디카아메' 가 추가되면 update가 나와

 

3)

그러면 mounted() 를 체감해보자!

beforeCreate(){console.log("beforeCreate")},
created(){console.log("created")},
beforeMount(){console.log("beforeMount")},
mounted(){

    this.load();
    console.log("mounted")
},
beforeUpdate(){console.log("beforeUpdate")},
updated(){console.log("updated")},
beforeUnmount(){console.log("beforeUnmount")},
unmounted(){console.log("unmounted")}

mounted() 안에서 load()를 호출한다.

data(){
    return{
        test:"hello",
        //모델을 준비해
        list: [
            {id:1,
                name:"아메리카노",
                price:5000
                },
                {id:2,
                name:"카페라떼",
                price:5500
                },
                {id:3,
                name:"카페모카",
                price:5500
                },
        ]
    };
},

methods:{
    //이름설정하고 핸들러 받기!
     categoryClickHandler(e){
        this.list.push({id:4, name:"디카 아메", price:5000})
    },

    load(cid){
        let promise = fetch("/menus");
        promise //그래서 promise에다가 함수를 담아주자!
        .then(qq => {

            return qq.json();
        })
        .then(list =>{
            // return ww[0];
            this.list = list;
        })

가장 아래 list 가 나오게끔하는거야

추가로 출력이 된 경우야

2. call 함수와 Object.assign()

(1) this 와 stric mode

1)

var x = 30;
console.log(window.x);

var f1 = function(){
    console.log(this);
}

f1();

이렇게 하면 log에 출력은 window야!

<!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>

    <!-- <script src="./test.js" defer="defer"></script> -->
    <script type="module" src="./test3.js" defer="defer"></script>
</head>
<body>
    
</body>
</html>

module이 붙여지면 strict mode야 스트릭모드는 엄격한 모드야 

지금까지 JS할때 어떤상태였냐 하면 변수를 선언해도 중복해도 오류가 안내 & 변수를 선언하지 않아도 오류가 안나

 

2)

그래서 ES6 strict 모드가 나왔어 모듈은 스트릭트 모드를 활용한다는 의미야

es5 에서 strict mode 사용방법
ES6 방식 strict 모드

 

(2) 디스트럭쳐링

1)

함수 선언에서 변수를 받는 쪽(인수)에서 중괄호를 쓰면은 객체를 받는다는 의미야!

add 는 객체를 받는다

(3) call

1) call 의 필요성

function createApp({data, methods}){
    let d = data();
    methods.f1();
};

createApp({
    data(){
        return{
            test:"hello"
        };
    },
    methods:{
        f1(){
            console.log(this);
        }
    }
})

여기서 log는 누구일까? f1이야! (객체에 따라서)

한편

function createApp({data, methods}){
    let d = data();

    methods.f1();
};

createApp({
    data(){
        return{
            test:"hello"
        };
    },
    methods:{
        f1(){
            console.log(this.test);
        }
    }


})

로 하면 

아무거도 나오지않는다. 왜냐하면 this의 범위가 methods 안 f1()으로 한정되어이있기 떄문이야

2)

function createApp({data, methods}){
    let d = data();

    methods.f1.call(d);
};

createApp({
    data(){
        return{
            test:"hello"
        };
    },
    methods:{
        f1(){
            console.log(this.test);
        }
    }


})

f1.call(d); 를 하면

 

call()을 하면은 this를 누구로 할지 정해주는거야

이 경우 () 안에 d를 넣으면 data()를 this로 한정한다는 거야

바로 앞과 상반되게 이 상태에서 log찍으면 data(){return}의 hello가 출력되는거야

다음과 같이 출력된다

마찬가지로 만약 this.test에서 this만 출력하면 return 객체 자체가 출력돼

function createApp({data, methods}){
    let d = data();

    methods.f1.call(d);
};

createApp({
    data(){
        return{
            test:"hello"
        };
    },
    methods:{
        f1(){
            console.log(this.test);
            this.f2();
        },
        f2(){
            console.log("hehe");
        }
}
})

이렇게 하면 f2()는 출력을 못해 다만 call()을 없애면 돼!

 

3) object.assign()

이 메소는 객체를 합쳐주는 역할이야

d가 합쳐서 저장되는 target이고 &  methods는 저장되기 이전 자원이야

3. 조건에 따라 화면에 다르게 binding 하기

지금까지는 직접 dom얻어서 classList에서 remove하는 방식이었어

 

(1) 버튼메뉴 누를 때 메뉴 추가하기

1)

<버튼 누를 때 메뉴 추가하기>

<section class="cart-section">
    <h1 class="d-none">장바구니</h1>
    <span class="text-title3">커피음료</span>				
    <a class="btn btn-mini bg-blue" @click.prevent="menuAddHandler" href="">메뉴추가</a>
</section>

onclick 를 심어주자

menuAddHandler(e){
    this.list.push({id:4, name:"디카 아메", price:5000})

    console.log("메뉴 추가");
}

methods 에다가 넣어주자!

이거를 누르면 메뉴리스트가 추가되는 방식이야

(2) 버튼메뉴 누를 때 숨기기

1)

<메뉴 추가 누르면 메뉴 추가 하는 상자가 없어지기>

숨겨뒀다가 메뉴추가버튼 누르면 다시 보이게끔 하자! (자주 쓸거면 숨기자! , 동적으로 추가하는거도 해보자)

학습위해서 클래스 보이게 하는거고 d-none을 넣었다 뻇다가 해보자

 

그런데 class 에다가 d-none을 메뉴추가 누를 때 사라지고 생기고를 하자 (dom조작 절대로 아니야)

<section class="menu menu-reg-section border-bottom border-color-1 " 

	:class="{ 'd-none':isShowRegForm }">

	<form class="overflow-hidden">
            <h1><input type="text" class="input w-75 w-100-md" name="titile" placeholder="메뉴 이름을 입력하세요."></h1> 
            <div class="menu-img-box">
                <img src="/image/product/blank-img.png" class="img-input">
                <input type="file" class="d-none">
            </div>
            <div class="menu-price"><input class="w-75 w-50-md input ml-0 ml-1-md" type="text" placeholder="가격을 입력하세요"> 원</div>
            <div class="menu-option-list">
                <span class="menu-option">
                    <input class="menu-option-input" type="checkbox">
                    <label>ICED</label>
                </span>
                <span class="menu-option ml-2">
                    <input class="menu-option-input" type="checkbox">
                    <label>Large</label>
                </span>
            </div>
            <div class="menu-button-list">								
                <input class="btn btn-line btn-round btn-size-1 btn-bd-blue rounded-0-md" type="submit" value="취소">
                <input class="btn btn-fill btn-round rounded-0-md btn-size-1 btn-bd-blue btn-color-blue ml-1" type="submit" value="저장">
            </div>
    </form>
</section>

class 를 추가하는지는 아래와 같이 설정하면 가능해

2)

<클래스와 스타일 바인딩방법>

:class="{ 'd-none':isShowRegForm }"

이렇게 하면 돼 이거는 d-none이 

Vue
.createApp({
data(){
return{
    test:"hello",
    //모델을 준비해
    list: [
        {id:1,
            name:"아메리카노",
            price:5000
            },
            {id:2,
            name:"카페라떼",
            price:5500
            },
            {id:3,
            name:"카페모카",
            price:5500
            },
    ],
    isShowRegForm:false
};
},
menuAddHandler(e){
    // this.list.push({id:4, name:"디카 아메", price:5000})
    this.isShowRegForm = (this.isShowRegForm == true)? false : true;
    console.log("메뉴 추가");

}

isShowRegForm이라는 model을 새로 만들자

menuAddHandlerController 가 가능해! true flase 값에 따라서 d-none여부가 달라지는거야

 

토글 처럼 이용이 가능해!

menuAddHandler(e){
    // this.list.push({id:4, name:"디카 아메", price:5000})
    // this.isShowRegForm = (this.isShowRegForm == true)? false : true;
    this.isShowRegForm = !this.isShowRegForm;
    console.log("메뉴 추가");

}

이렇게 해서 !를 하면 not 으로 역연산이된다. (나는 삼항연산자)

 

(3) 저장하기 누르면 저장하기

1)

2way bind방법으로 v-model을 쓰자! 그 이전에 저장버튼 누르면 작동되는 handler를 만들자!

 

<model 과 핸들러 연결>

<div class="menu-button-list">								
    <input class="btn btn-line btn-round btn-size-1 btn-bd-blue rounded-0-md" type="submit" value="취소">
    <input class="btn btn-fill btn-round rounded-0-md btn-size-1 btn-bd-blue btn-color-blue ml-1" type="submit" value="저장" @click.prevent="menuSaveHandler">
</div>
menuSaveHandler(e){
    console.log("메뉴 추가");
}

 

2)

<바인딩>

그리고 2way용으로 v-model에다가 모델을 담아주자!

<form class="overflow-hidden">
<h1><input type="text" class="input w-75 w-100-md" name="name" placeholder="메뉴 이름을 입력하세요." v-model="menu.name"></h1> 
<div class="menu-img-box">
    <img src="/image/product/blank-img.png" class="img-input">
    <input type="file" class="d-none">
Vue
.createApp({
	data(){
		return{

			menu:{name:"우좌진", price:9900},
		};
	},

v-model = menu.name 해주면 다음과 같이 바인딩이 돼

 

3)

지금 등록을 위한 박스기 아이템으로 뒀자나! 다음주는 모달로 띄울꺼야 이런경우 큰 차이가 없어!

모달을 띄우든 어떻게 하든 원하는대로 하면되는데 방법은같아!

 

아무튼 이제 메뉴추가를 해보자 우선 Postman으로 메뉴 추가를 만들어보자 (테스트 및 소스코드까지)

post 방식으로 하는거야! (post 는 create 이기 때문)

바디에 심어준다.

이렇게 했을 때 서버가 어떻게 되는지 알아보는거야 정확하게 써야해!

@PostMapping
public String insert(String name, int price) {
    System.out.println(name);
    return "menu insert";

};

이렇게 해서 name이 "아메리카노"로 출력이될까?

500 error

되지않아

콘솔을 보면 price 가 나오는데 insert()메소드에서 name이 다 받아버리고 그다음 인수인 price로 못가서 그래

json통으로 받아버려

@PostMapping
public String insert(Menu menu) {
    System.out.println(menu.getName());
    return "menu insert";

};

menu에 담아서 getName()을 하면 출력이 될까?

postman에 담는건 돼

그런데 콘솔에는 null이라고 나왔어!

이건 전송은 됐지만! 사실 처음부터 값을 담지 못했다는 말이야!

 

4)

@PostMapping
public String insert(@RequestBody Menu menu) {
    System.out.println(menu.getName());
    return "menu insert";

};

@ReauestBody를 해줘 (비동기처리하는경우 이용)

media Type에러야!

이거에 대해서 type을 설정해주어야해

type을 json 타입으로 설정해주자!

 

잘 출력이 되었어! 그러면 이런방식으로 post man에서 코드로 바꾸는것을 볼 수있어

우측 코드스니펫handler에다가 넣어보자

menuSaveHandler(e){

//타입설정
    var myHeaders = new Headers();
    myHeaders.append("Content-Type", "application/json");

//json타입으로 변환
    var raw = JSON.stringify(this.menu)

//요청 옵션
    var requestOptions = {
    method: 'POST',
    headers: myHeaders,
    body: raw,
    redirect: 'follow'
    };

//실질 데이터 요청부터 출력
    fetch("http://localhost:8080/menus?", requestOptions)
    .then(response => response.text())
    .then(result => console.log(result))
    .catch(error => console.log('error', error));

}

이렇게 입력후에 저장누르면
출력성공

이렇게 출력이 된것을 볼 수있다.

 

8)

이렇게 하고 나서 service로 가서 삽입을 해보자!

 

 


1. 보충

(1) @RequestBody ( https://cheershennah.tistory.com/179 )

What (정의) : 스프링에서 비동기처리로 요청 및 응답을 한다 이 경우 담는 곳이 body이고 요청응답 각각을 requestBody, responseBody라고 한다.

Why (존재이유) : 비동기 통신 때 요청 및 응답을 담기위해서이다. 특히 본문에 담기는 형식으로 JSON이 있다

How (방법) : 요청시 JSON을 자바 객체로 반환한다. 응답시에는 자바객체를 JSON으로 변환한다

 

 

2. 회고 

1) 오전에 타임리프와 Vue중 동시에 쓰면 어떻게 될지에 대해서 이야기를 했는데 실제로 수업중에 나와서 반가웠다!

 

2) 실제로 화면에 쉽게 뿌려주는 방법이 신기했다

 

3) api 사용에 대해서 낯설다 저렇게 정말로 Restcontroller로 하는것이 신기하다

 

 

 

 

 

 

 

 

 

'배움 __IL > TIL 1기' 카테고리의 다른 글

TIL : 83번째- 230404 [4-1-화]  (0) 2023.04.04
TIL : 82번째- 230403 [4-1-월]  (0) 2023.04.03
TIL : 80번째- 230330 [3-4-목]  (0) 2023.03.30
TIL : 79번째- 230329 [3-4-수]  (0) 2023.03.29
TIL : 78번째- 230328 [3-4-화]  (1) 2023.03.28