728x90

#Vue 인스턴스 생성

 

[Vue2]

//Vue2 application instance 생성방법
new Vue({
	//component options
    el: '#app', // mount할 element
    template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',, //화면에 표시할 요소
    data: function() {
    	return {
        	firstName: 'choi',
            lastName: 'coding',
            alias: 'choicoding'
        }
    },
    method: {
    	changeFirstName(first) {
        	this.firstName=first
        },
    }, //화면의 동작, 이벤트 handling할 메소드 
    created: function(){}, //life cycle hook (beforeCreate, beforeMount, mounted, ..)
    watch: {
    	firstName: function (val, oldVal) {
        	console.log(oldVal,'->',val);
        }
    }, //data의 변화를 감시하여 대응할 동작

});

new Vue({
    data: {
    },

}).$mount('#app');

 

[Vue3]

//Vue3 application instance 생성 방법
Vue.createApp({
	data() {
    	return {
           	a: 'adata'
        }
    }
}).mount('#app');

 

 

#Vue Directive[ v-on ]

 

이벤트함수 등록

 

v-on:이벤트="function" 

 

>> v-on:은 @으로 대체될 수 있다. 

ex) @click="function"

<div id="app">
    <button v-on:click="minus">-</button>
    <p>{{count}}</p>
    <button v-on:click="plus">+</button>
</div>
<script>
    Vue.createApp({
        data(){
            return{
                count: 0
            }
        },
        methods: {
            plus(){
                this.count++;
            },
            minus(){
                this.count--;
            }
        }
    }).mount('#app');
</script>

 

#Vue Directive[ v-for ]

 

리스트 렌더링

 

v-for="alias in list

 

<div id="app">
    <ul>
        <li v-for="item in items">{{item}}</li>
    </ul>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        data(){
            return{
                items: ['1번','2번','3번','4번']
            }
        }
    }).mount('#app');
</script>

실행결과

 

**shortcut

ul>li +tap : ul와 하위 li 태그 동시 생성

 

 

#Vue Directive[ v-if / v-else-if / v-else ]

 

조건부 렌더링

 

v-if="조건

<body>
    <div id="app">
        <p v-if="a">a가 존재하면 렌더링될 태그</p>
        <p v-else-if="a==10">a=10이면 렌더링될 태그</p>
        <p v-else>다 아니면 렌더링될 태그</p>
    </div>
</body>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        data(){
            return{
                a:1
            }
        }
    }).mount('#app');
</script>

 

v-show="조건"

 

 <p v-show="a">a가 존재하면 렌더링될 태그</p>

 

v-if 와 v-show의 차이점

 

v-if는 조건에 따라서 선택적으로 렌더링되고 

v-show는 일단 렌더링되고 조건이 false이면 css로 display:none 이 된다. 

 

v-if는 전환비용이 높기 때문에 만약 사용자가 v-if의 조건을 자주 바꾼다면 

v-if가 전환될 때마다 재렌더링을 해야한다. 

반면, v-show는 최초 렌더링 할때 같이 렌더링해두고 css만 전환하면 되기 전환비용이 낮다. 

대신 최초 렌더링할때 비용이 더 높기 때문에 

전환이 자주 발생한다면 v-show를, 전환비용보다 최초 렌더링시 비용이 더 크다면 v-show를 선택하는 것이 좋다. 

 

#v-if 와 v-for 함께 사용하기 

v-if와 v-for를 함께 사용하는 것은 권장되지 않는다. 

//잘못된 코드
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
  >
    {{ user }}
  </li>
</ul>

위 코드의 목표는 v-for로 users를 순회하면서 user의 isActive로 조건을 걸어 리스팅하는것이다.

하지만 위 코드를 실행하면 user프로퍼티를 찾을 수 없어 오류가 발생한다. 

v-if가 v-for보다 우선순위가 높기 때문에

users의 user를 설정하기 전에 if의 user 프로퍼티를 먼저 찾기 때문에 찾을 수 없다는 것이다. 

 

v-if와 v-for를 함께 사용하기 위해선

users를 먼저 필터링하거나 template 태그를 이용해 for지시어를 먼저 선언해주는 방법이 있다. 

 

* computed property 사용하기

<ul>
  <li
    v-for="user in activeUsers" //계산된 프로퍼티 내를 순회한다.
  >
    {{ user }}
  </li>
</ul>

computed: {
  activeUsers() { //users를 필터링하여 activeUsers를 정의한다. 
    return this.users.filter(user => user.isActive)
  }
}

 

* template tag 사용하기 

 

<ul>
  <template v-for="user in users"> //li를 래핑하고 li의 상위 레벨에서 for지시어를 사용함으로써 if보다 우선하게 한다
    <li v-if="user.isActive">
      {{ user }}
    </li>
  </template>
</ul>

 

#Vue Directive[ v-bind ]

데이터, id, class, style 바인딩 

 

   Vue.createApp({
        data(){
            return {
                textClass: 'primary',
                sectionid: 'tab1',
                sectionStyle: {color:'red'},
                isActive:true,
                hasError:false,
                classObject:{
                    active:true,
                    'text-danger':false
                }
            }
        }
    }).mount('#app');

 

* v-bind: 은 매우 자주 사용되기 때문에 v-bind를 생략하고 :만 붙여 사용할 수 있다. 

v-bind를 이용하면 컴포넌트의 data를 바인딩 할 수 있다.

v-bind는 컴포넌트의 {{property}}뿐만 아니라 id, class, style 등 태그의 속성도 바인딩할 수 있다.

바인딩하는 data는 객체 또는 배열도 가능하다.

 

<div :class="[textClass, {active: isActive}]">클래스 바인딩</div>

 

active의 클래스 여부가 isActive에 의해 결정된다. 

isActive가 true이므로 :class="[textClass, active] 배열이 되고 

class="primary active"가 된다. 

<div :id="sectionid" :style="sectionStyle">아이디 스타일 바인딩</div>

 

아이디와 스타일 바인딩의 예시이다. 

아이디는 문자열로, 스타일은 객체로 바인딩 되었다. 

 

<div class="static" :class="classObject">클래스 객체로 바인딩</div>

 

class="static"이 이미 존재하고 classObjecct가 바인딩된다. 

이때, classObject는 true인 active만 바인딩되고 false인 'text-danger'는 바인딩 되지 않는다. 

바인딩 결과는 static active가 된다.

 

<div :class="isActive ? textClass : ''">삼항연산 클래스 토글</div>

삼항 표현식을 사용할 수도 있다.

isAcitve이면 textClass가 바인딩된다.

삼항 조건의 결과값에는 문자열 또는 바인딩될 data가 들어간다. 

 

 

#Vue Component 등록

 

app이라는 root 컴포넌트에 자식 컴포넌트들을 등록한다.

 

components: {

   컴포넌트명: {body}

}

<div id="app">
    <app-header></app-header>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    Vue.createApp({
        components: {
            //'component이름' :{body}
            'app-header': {
                template: '<h1>app 하위 컴포넌트 "app-header"</h1>'
            }
        }
    }).mount('#app');
</script>

#app 태그 내에  

<component명으로 태그를 작성한다.>

실행결과

dev tool의 Vue에 

<Root> 하위 <AppHeader>컴포넌트가 등록된 것을 확인할 수 있다.

 

 

 

#Component간 통신

 

Component는 event와 props로 데이터와 신호를 주고받는다. 

 

동일 레벨의 컴포넌트 간에 직접 통신은 불가능하다. 

상위 컴포넌트에 event를 발생시켜 데이터를 전달할 다른 하위 컴포넌트로 prop을 전달하는 방식으로 통신한다. 

 

Props

 

상위 컴포넌트에서 하위 컴포넌트에 전달하는 프로퍼티

 

* 하위 프로퍼티에 props 배열을 작성한다.

* html의 컴포넌트 태그에서 props를 바인딩한다. 

 

v-bind:prop명="prop에 바인딩할 상위 컴포넌트의 프로퍼티명"

 

<div id="app">
    <app-header v-bind:prop-title="title"></app-header>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const appHeader = {
        props:['propTitle'],
        template: `<p>{{propTitle}}</p>`,
    }
    Vue.createApp({
        data(){
            return {
                title: 'Title'
            }
        },
        components: {
            'app-header': appHeader,
        }
    }).mount('#app');
</script>

 

EventEmit

 

하위 컴포넌트에서 상위 컴포넌트로 이벤트를 발생시킴

 

* 하위 컴포넌트에서 this.$emit('발생시킬이벤트명') 을 작성한다.

* html의 컴포넌트 태그에서 이벤트 핸들러로 상위 컴포넌트에 메소드를 동작하게 한다. 

 

v-on:하위컴포넌트에서 발생시킨 이벤트명="상위컴포넌트에서 동작할 메소드명"

 

<div id="app">
    <app-header :prop-title="title"></app-header>
    <app-contents @set-title="changeProp"></app-contents>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const appHeader={
        props:['propTitle'],
        template:`<p>{{propTitle}}</p>`
    }
    const appContents={
        template: `<button v-on:click="sendEvent">제목</button>`,
        methods:{
            sendEvent(){
                this.$emit('setTitle');
            }
        }
    }
    Vue.createApp({
        data(){
            return{
                title:''
            }
        },
        methods:{
            changeProp(){
                this.title='newTitle'
            }
        },
        components:{
            'app-header':appHeader,
            'app-contents':appContents
        }
    }).mount('#app');
</script>

 

동일 레벨의 컴포넌트간 통신이 이루어지는 과정

 

-> appContents내의 버튼에서 클릭이벤트가 발생하면

-> appContents 컴포넌트에 setTitle 이벤트를 발생시킨다. 

->  setTitle 이벤트 핸들러에 의해 Root 컴포넌트의 changeProp함수가 실행된다.

->  changeProp 함수가 data를 갱신하고 

->  data가 변경됨에 따라 props의 propTitle도 변경된다. 

 

 

**html 태그 작성시 주의할점 

html 파일에서 vue를 사용할 때 카멜케이스를 사용하면 인식하지 못하기 때문에 -(하이픈)을 사용한다.

이러한 문제는 .vue 파일 형식과는 관계없이 .html 파일에만 해당한다. 

.vue 파일에는 카멜케이스로 작성하여도 된다. 

 

<div id="app">
    <app-contents @set-title="changeProp"></app-contents> //setTitle을 set-title로 changeProp은 속성명이 아닌 ""내의 문자열 값이기 때문에 관계 없다.
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
    const appContents={
        template: `<button v-on:click="sendEvent">제목</button>`, 
        methods:{
            sendEvent(){
                this.$emit('setTitle'); //카멜케이스
            }
        }
    }
    Vue.createApp({
        methods:{
            changeProp(){ //카멜케이스
                this.title='newTitle'
            }
        },
        components:{
            'app-contents':appContents //컴포넌트명은 html에 작성될 태그명이므로 - 사용
        }
    }).mount('#app');
</script>

 

 

'Vue.js' 카테고리의 다른 글

Vue.js form submit  (0) 2023.11.30
Vue.js 프로젝트 생성하기  (0) 2023.11.29
Vue.js Intro  (0) 2023.11.28
728x90

Vue.js

UI, 라우팅, SSR( Server Side Rendering ) 등의 프론트엔드 개발을 지원하는 프레임워크


Vue.js 공식문서

 

#Vue3 코드 작성 방식

OptionsAPI, CompositionAPI 두가지 방식이 존재하고 

공식문서의 네비게이션바에서 각 방식에 따른 API 설명을 확인할 수 있다. 

 

-OptionsAPI

 

클래스 기반 컴포넌트 인스턴스(this) 구조 

<script>
export default {
  // data()에서 반환된 속성들은 반응적인 상태가 되어 `this`에 노출됩니다.
  data() {
    return {
      count: 0
    }
  },

  // methods는 속성 값을 변경하고 업데이트 할 수 있는 함수.
  // 템플릿 내에서 이벤트 헨들러로 바인딩 될 수 있음.
  methods: {
    increment() {
      this.count++
    }
  },

  // 생명주기 훅(Lifecycle hooks)은 컴포넌트 생명주기의 여러 단계에서 호출됩니다.
  // 이 함수는 컴포넌트가 마운트 된 후 호출됩니다.
  mounted() {
    console.log(`숫자 세기의 초기값은 ${ this.count } 입니다.`)
  }
}
</script>

<template>
  <button @click="increment">숫자 세기: {{ count }}</button>
</template>

 

 

-CompositionAPI

 

재사용성이 좋은 컴포넌트 구조

<script setup>
import { ref, onMounted } from 'vue'

// 반응적인 상태의 속성
const count = ref(0)

// 속성 값을 변경하고 업데이트 할 수 있는 함수.
function increment() {
  count.value++
}

// 생명 주기 훅
onMounted(() => {
  console.log(`숫자 세기의 초기값은 ${ count.value } 입니다.`)
})
</script>

<template>
  <button @click="increment">숫자 세기: {{ count }}</button>
</template>

 

 

#개발 환경

 

Node.js https://nodejs.org/en

 

Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

Visual Studio Code https://code.visualstudio.com/ 

 

Visual Studio Code - Code Editing. Redefined

Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications.  Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows.

code.visualstudio.com

 

*plugins 

- Vue VSCode Snippets

- Live Server

- Material Icon Theme 

- Night Owl (테마)

- ESLint, TSLint Vue (문법 어시스턴스), Auto Close Tag

- Prettier( Code Formatter ), Project Manager

- GitLens

- Atom Keymap, Jetbrains IDE Keymap ( Shortcut )

[ Vue2 - Vetur / Vue3 - Volar ] 

 

 

 

WindowsOS 선택사항

(cmder https://cmder.app/)

 

Cmder | Console Emulator

Total portability Carry it with you on a USB stick or in the Cloud, so your settings, aliases and history can go anywhere you go. You will not see that ugly Windows prompt ever again.

cmder.app

 

Chrome Extension Devtool

Vue.js devtools

https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd

 

Vue.js devtools

Browser DevTools extension for debugging Vue.js applications.

chrome.google.com

 

 

CDN( Content Delivery Network )

https://ko.vuejs.org/guide/quick-start.html

 

빠른 시작 | Vue.js

 

ko.vuejs.org

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

 

**

vscode shortcut

#app + tap

tag_name + tap

 

 

기본 예제

- OptionsAPI

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app">{{ message }}</div>

<script>
  const { createApp } = Vue //distructuring 문법

  createApp({ //vue를 사용할 application instance
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app') // id에 app인 태그에 mount(부착)
</script>

 

 

SSR도 가능하다더니 thymeleaf 사용하는 느낌이 난다.. 

message라는 프로퍼티와 mount 함수 프로퍼티를 가진 createApp 클래스를 만들고 

html에 표현식으로 createApp 객체의 프로퍼티를 사용해 렌더링 하는 느낌이다. 

 

distructuring 대신 Vue.createApp으로 간단하게 사용할 수 있다. 

<script>
    Vue.createApp({
        data(){
            return {
                message: 'hello vue'
            }
        }
    }).mount('#app');
</script>

 

 

-CompositionAPI

<script>
  const { createApp, ref } = Vue

  createApp({
    setup() {
      const message = ref('Hello vue!')
      return {
        message
      }
    }
  }).mount('#app')
</script>

 

 

#Vue 작동 원리 ( reactivity ) 

 

javascript의 Proxy Api를 사용하여 Vue의 작동원리를 알아본다. 

 

 const data={
    a:10
   }

   const app = new Proxy(data,{
    get() {
        console.log('프록시를 이용해 mocking data의 값에 접근하면 나오는');
    },
    set() {
        console.log('실제 data에는 변경되지 않지만 프록시를 이용해 mocking data의 값을 변경하면 나오는');
    }
   });

실행결과

Proxy는 data를 mocking할 뿐, 실제 data에 접근하는 것이 아니기 때문에 실제 data의 값은 변함 없다.

 

   const data={
    a:10
   }
   function render(newValue){
    let app = document.querySelector('#app');
    app.innerHTML=newValue;
   }
   const app = new Proxy(data,{
    get() {
        console.log('프록시를 이용해 mocking data의 값에 접근하면 나오는');
    },
    set(target,prop, newValue) {
        target[prop] = newValue; //data.a=newValue
        render(newValue); //app이라는 객체의 값이 바뀌면 dom 객체의 html이 바뀐다. 
    }
   });

 

app이란 객체는 app이라는 div 태그의 html을 동적으로 변경해주는 객체가 된다. 

 

즉, 나는 동적으로 바뀔 부분을 컴포넌트로 코드를 작성하고, 그 컴포넌트(App)를 

돔 트리의 특정 객체(div id='App')로 만들면

Vue라는 프록시가 ref의 값을 감시하다가 값이 변경되면

그 돔 객체의 html을 수정해준다. 

 

여기서 주의할 점은 Vue라는 프록시를 통해 값을 변경해야 돔 객체를 수정해주지

직접 ref의 원본인 data에 접근하여 변경하면 render()를 실행하지 않기 때문에 돔 객체를 수정해주지 않는다. 

 

'Vue.js' 카테고리의 다른 글

Vue.js form submit  (0) 2023.11.30
Vue.js 프로젝트 생성하기  (0) 2023.11.29
Vue.js 기초 문법  (2) 2023.11.28
728x90

이번 프로젝트에서는 AWS의 ElasticBeanstalk 을 이용해 application을 배포하고 

github actions를 이용해 aws에 무중단 배포할 수 있는 환경을 구축하였다. 

 

AWS의 서비스에 관해서는 이미 다룬 적이 있으니 생략하고 

https://youarethebestcoding.tistory.com/119

 

프로덕션 배포 - 서비스 아키텍처

EC2 (Elastic Computer Cloud) : 애플리케이션을 실행할 수 있는 서버 컴퓨터라고 생각하면 된다. 우리는 EC2로 서버를 실행시키고 클라이언트가 애플리케이션을 실행하려면 EC2의 IP나 EC2에서 제공하는

youarethebestcoding.tistory.com

 

이번 포스팅에서는

aws cli로 환경을 생성하는 두가지 방법을 모두 설명하고 

github actions 까지 다뤄본다. 

 

 

#배포를 위한 애플리케이션 환경 설정

 

 

 

- properties(yaml) 파일 분리 

 

배포를 할 때의 환경과 개발의 환경이 다른 경우 

profile을 분리해주어야한다. 

 

스프링부트의 profile은 'application-' 접두어 뒤에 단어로 구분한다. 

 

보통 application- [local, dev, prod...] 와 같이 naming 한다. 

 

본인은 개발환경의 profile을 local로 해주었다. 

 

보통 profile을 분리하는 이유가 개발환경과 배포환경의 설정, 연동할 db 때문에 분리하게 되는데 

 

profile을 분리하고 바로 실행을 하려하면 스프링부트는 어떤 profile로 실행해야할지 알지 못하기 때문에 애플리케이션을 실행하지 못한다. 

 

profile을 설정하는 방법은 

application.properties 파일을 작성해서 

 

<application.yaml>
spring:
  profiles:
    active: local
    
<application.properties>
spring.profiles.active=local

active할 profile을 명시해주거나 

 

이클립스 기준

Run-Run configurations에서 

Profile을 지정해주면 된다. 

profile이 없다면 빈칸으로 되어있을것이고 

 

application.properties를 사용하고 싶다면 Profile에 default를 선택하면 된다. 

만약 application.properties와 -dev, -local이 있고 default에 active profile이 명시되어있다면 

default를 사용하거나 사용할 profile을 직접 선택해주면 된다. 

 

보통 application.properties에 

모든 환경에서 공통으로 설정할 부분을 적고 profile에 각 프로필마다 사용할 설정을 하기 때문에 

 

application.properties에 active할 profile을 지정해주고 default를 run하는게 좋을것 같다. 

 

 

server.port=5000
spring.jpa.database=MYSQL
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

spring.datasource.url=jdbc:mysql://${rds.hostname}:${rds.port}/${rds.db.name}
spring.datasource.username=${rds.username}
spring.datasource.password=${rds.password}

[ application-prod.properties ]

 

일단 aws의 nginx는 5000포트를 default로 사용하기 때문에 

포트를 5000번으로 설정하고 

 

dev 환경의 데이터베이스는 학원의 oracle db를 사용했지만 

prod 환경에서는 aws rds의 mysql을 사용할 것이기 때문에 

database를 MYSQL로 변경하고 

url, username, password는 rds 콘솔에서 엔드포인트, 포트를 확인하여 하드코딩할 수 있지만

rds로부터 변수를 받아 입력할 수 있게 

${rds.variable}를 이용한다. 

 

env.properties는 민감한 properties들을 입력해놓은 파일이다. 

 

@Configuration
@PropertySources({@PropertySource("classpath:envs.properties")})
public class PropertyConfig {

}

 

env.properties는 gitignore에 등록하고 jar 파일을 배포하고

이후 github actions을 통해 무중단 배포를 할 때는 git에 올라오지 않기 때문에 

git의 repository variable을 이용해 동적으로 파일을 생성해서 배포할 것이다. 

 

- build.gradle에 mysql을 사용하기 위해 mysql connector를 import 해준다. 

implementation 'com.mysql:mysql-connector-j:8.1.0'

 

 

#cors 을 위한 설정 

// 스프링 서버 전역적으로 CORS 설정
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
        	.allowedOrigins("http://localhost:5000") // 허용할 출처, 이후 배포된 사이트의 도메인을 추가
            .allowedMethods("GET", "POST", "PUT", "DELETE") // 허용할 HTTP method
            .allowCredentials(true) // 쿠키 인증 요청 허용
            .maxAge(3000); // 원하는 시간만큼 pre-flight 리퀘스트를 캐싱
    }

 

 

#AWS CLI 다운로드 및 배포

 

AWS 계정 생성 후 AWS console에 IAM 으로 1로그인 

++IAM ( Identity and Access Management ) 

기업에서 많은 개발자가 접근할 수 있는 계정이 있어야 하는데 만약 기업 계정에 모든 개발자가 아이디와 비밀번호를 알고 접근할 수 있게 하는것은 위험하므로 

사람마다 역할마다 다른 접근 권한을 부여할 수 있게 한다. 

사람에게는 아이디와 비밀번호, 프로그램에게는 액세스 키와 비밀 액세스 키를 제공 

 

 

 

AWS CLI, EB CLI 설치를 위해 python 설치 

AWS CLI, EB CLI는 파이썬 기반으로 작동 

 

파이썬 설치 

https://www.python.org/downloads/

 

Download Python

The official home of the Python Programming Language

www.python.org

 

파이썬 설치 버전 확인 

python --version 커맨드 입력 

 

AWS CLI 설치 

https://aws.amazon.com/ko/cli/

 

Command Line Interface - AWS CLI - AWS

aws-shell은 명령줄 셸 프로그램으로서, AWS 명령줄 인터페이스를 사용하는 새로운 사용자와 고급 사용자 모두에게 도움이 되는 편의 기능 및 생산성 기능을 제공합니다. 주요 기능은 다음과 같습

aws.amazon.com

 

설치 후 터미널 재실행 후 aws --version 확인 

 

콘솔에 로그인한 후 서비스 검색창에 IAM 검색하여 IAM 대시보드로 들어간다. 

 

우리가 사용할 CLI 는 AWS에 접근해 우리의 커맨드를 대신 실행해줄 프로그램이므로 IAM으로부터 액세스 키와 비밀 액세스 키를 발급 받아야 한다. 

 

액세스 관리 - 사용자 - 사용자 생성 

 

 

사용자 이름을 입력하고 IAM 사용자를 생성해 AWS Management Console에 접근 권한을 제공한다.

++ AWS Management Console 접근 권한 제공 체크할 필요 없다. 

aws가 바뀌면서 프로그래밍 방식 액세스 키를 발급 받는 작업은 사용자를 생성한 후 한다. 

AWS Management Console 접근 권한은 접근 비밀번호를 지정하는 사용자가 되는 것이다. 

CLI는 프로그램이라 필요가 없다.

 

직접 정책에 연결을 선택하고 

AdministratorAccess  정책을 선택하고 다음으로 넘어간다. 

태그를 생성하고 싶다면 생성하고 다음으로 넘어간다. 

태그는 사용자에 추가될 수 있는 키-값 페어로 이메일이나 직급 등의 내용을 포함하고 싶을 때 사용할 수 있다. 

 

++ 정책 Policy 

권한을 나열한 문서 

AdministratorAccess는 모든 AWS rss에 접근할 수 있는 모든 권한을 준다는 뜻이다. 

AdministratorAccess-Amplify는 Amplify에 접근할 수 있는 모든 권한을 준다는 뜻이다. 

생성된 사용자를 클릭해 세부 페이지로 들어오면 액세스키가 나오고 액세스 키가 없다면 생성하는 버튼이 나온다. 

CLI를 선택하고 설명 태그 값에는 CLIaccessKey라고 입력했다. (선택사항) 

 

발급받은 액세스키와 비밀 액세스 키를 메모해둔다. 

비밀 액세스 키는 다시 확인할 수 없다. 

 

터미널에서 aws configure 명령어로 aws cli를 설정해준다. 

 

C:\Users\USER>aws configure
AWS Access Key ID [None]:
AWS Secret Access Key [None]: 
Default region name [None]: ap-northeast-1
Default output format [None]: json

 

Default region : AWS 데이터센터가 있는 장소로 서비스를 실제로 사용할 사용자와 서비스가 호스팅되고 있는 데이터 센터가 가까울수록 네트워크 대기 시간이 짧아진다. 사용자가 한국에 거주할 확률이 높다면 ap-northeast-2를 선택하면 된다. 

 

AWS CLI는 AWS의 모든 서비스를 위한 CLI 이고 

EB CLI는 일래스틱 빈스톡만을 위한 CLI이다. 

EB CLI로 일래스틱 빈스톡 환경을 구축하고 설정할 수 있다. 

 

pip 를 이용해 awsebcli 설치 

 

pip install awsebcli --upgrade --user 

 

설치한 후 Warning에 환경변수를 추가하라고 나온다. 

 

환경변수의 하단에 위치한 시스템 변수 Path에서 

%USERPROFILE%\AppData\Roaming\Python\Python11\Scripts를 추가한다.

C:\Users\USER\AppData\Roaming\Python\Python311\Scripts도 추가해준다. 

 

 

파이썬 12는 지금 awsebcli 설치하면 오류가 생긴다. 

버전 차이 때문에 그런거 같은데 진짜 너무 고생했다... 

파이썬 11로 다운그레이드하고 다시 시도해서 겨우 설치했다. 

 

프로젝트의 위치에서 eb init 명령어로 일래스틱 빈스톡 환경 초기화 

 

select default region 

 일래스틱 빈스톡이 어느 리전에 이 애플리케이션의 환경을 생성해야 하는지 물어보는 것 

이후 생성되는 모든 리소스는 지금 선택하는 리전에 생성된다. 

 

do you want to set up SSH for your instance? 

 일래스틱 빈스톡을 이용해 생성된 ec2에 접근하기 위해 ssh를 설정할 것인지 물어보는 것 

일단 이 프로젝트에선 설정하지 않는다. 

 

init을 완료하면 project directory에 .elasticbeanstalk 디렉터리가 생성되고

그 안에 init에서 설정한 내용이 들어있는 config.yml 파일이 생성된다. 

 

 

이제 배포할 애플리케이션을 jar 파일로 build해주어야 한다. 

 

1. cmd cli 

프로젝트의 위치에서 

gradlew build 

 

2. IDE tool 이용하기 

eclipse 기준 

gradle tasks - build - build (또는 bootJar)

 

build 이후에 

디렉토리의 build/llibs에 jar 파일이 생성된다. 

 

다시 .elasticbeanstalk 의 config.yml 파일에 

branch-defaults:
  master:
    environment: Danaga-env
    group_suffix: null
deploy:
  artifact: build/libs/danaga-0.0.1-SNAPSHOT.jar
global:
  application_name: danaga-app
  branch: null
  default_ec2_keyname: null
  default_platform: Corretto 17 running on 64bit Amazon Linux 2023
  default_region: ap-northeast-1
  include_git_submodules: true
  instance_profile: null
  platform_name: null
  platform_version: null
  profile: eb-cli
  repository: null
  sc: git
  workspace_type: Application
deploy:
  artifact: build/libs/danaga-0.0.1-SNAPSHOT.jar

deploy:artifact: jar파일 을 추가한다. 

 

 

eb create --database --elb-type application --instance-type t2.micro 명령어로 환경 생성 

 

--database : 생성하는 환경에 RDS 데이터베이스를 만들기 위한 매개변수, 자동으로 데이터베이스가 생성된다. 

--elb-type : 일래스틱 로드 밸런서 타입 매개변수, application,, classic, network가 있다. 

https://aws.amazon.com/ko/elasticloadbalancing/features/?nc=sn&loc=2 

 

네트워크 트래픽 분산 - Elastic Load Balancing - Amazon Web Services

 

aws.amazon.com

--instance-type : 애플리케이션이 동작할 인스턴스 타입, 프리티어에서 제공하는 t2.micro 사용 

 

eb create --database --elb-type application --instance-type t2.micro

 

DNS CNAME prefix는 url이므로 이미 사용중인 url과 중복될 수 없어 본인이 고유값을 입력해준다. 

 

would you like to enable spot fleet requests for this environment : N

 

 

정상적인 경우라면 성공적으로 배포했을텐데 

 

나는 배포에 실패하고 severe 상태가 되었다. 

 

사이트에 접속하면 502 bad gateway가 뜬다. 

 

이럴때는

로그를 확인하면 된다. 

 

로그 요청을 눌러서 발행된 로그에 다운로드를 클릭해서 로그를 확인할 수 있다. 

 

나같은 경우는 application profile을 설정하지 않아서 배포에 실패했다. 

프로젝트 당시 처음 배포했을 때는 thymeleaf 템플릿에 문제가 있어 로그를 계속 확인하면서 몇번을 고쳤다. 

 

개발환경에서는 타임리프에 아무 문제 없고 사이트의 서비스에 아무 문제가 없었는데 

배포할때는 개발환경에서 문제가 되지 않는 사소한 표현식에서 문제가 되었다. 

 

#Github actions 

 

Github action은 repository에서 workflow를 자동으로 할 수 있게 지원하는 깃헙의 툴이다.

 

workflow는 event, runner, jobs와 steps으로 이루어져있다. 

 

event가 발생하면 runner가 job을 실행하고 job은 step들로 이루어져있다. 

 

하나의 runner는 하나의 job을 실행하고 step은 순차적으로 진행된다. 

그렇기 때문에 step끼리는 데이터가 전달될 수도 있다. 

 

job내의 step들은 순차적으로 진행되지만 job끼리는 기본적으로 독립적으로 진행된다. 

물론 어떤 job이 실행된 이후에 실행되어야할 다른 job이 있다면 packaging 하여 종속시킬수 있다. 

 

 

 

 

https://docs.github.com/ko/actions/learn-github-actions/understanding-github-actions#the-components-of-github-actions

 

GitHub Actions 이해 - GitHub Docs

GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository, or deploy merged

docs.github.com

 

#secrets and variables

 

github엔 민감한 정보를 secret으로 설정하고 변수처럼 사용할 수 있는 기능이 있다.

 

repository의 setting - security - secrets and variables - actions - new repository secret

 

위에 배포환경 profile에 대해 설명할 때 나온 env.properties 와 application.properties를 애플리케이션이 아닌 

secret에 설정하고 workflow에서 배포할 때 파일을 만든 후 애플리케이션을 build 한다. 

 

배포 과정에서 aws access key id와 aws secret access key 가 필요하므로 설정해준다. 

 

이렇게 한번 만들어주면 다시 들어가도 이전에 초기화해둔 값을 확인하지 못하기 때문에 

repository를 공유하는 팀원간에도 민감한 정보의 유출을 막으면서 변수를 사용해 값을 사용할 수 있다. 

 

 

 

#workflow 작성

 

레포지토리의 actions에 들어가면 new workflow를 작성할 수도 있고

깃헙에서 제공하는 템플릿을 이용해 작성할 수도 있다.

 

 

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a package using Gradle and then publish it to GitHub packages when a release is created
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#Publishing-using-gradle

name: Java CI with Gradle

on:
  push:
    branches:
      - master
      
permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Run chmod to make gradlew executable
      run: chmod +x ./gradlew
      
    - name: make application.properties
      run: |
          cd ./src/main/resources
          touch ./application.properties
          echo "${{ secrets.APP_PROPERTIES }}" > ./application.properties
     
    - name: make env.properties
      run: |
          cd ./src/main/resources
          touch ./env.properties
          echo "${{ secrets.ENV_PROPERTIES }}" > ./env.properties

    - name: Build with Gradle
      uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
      with:
        arguments: clean build bootJar

    - name: Get current time
      uses: 1466587594/get-current-time@v2
      id: current-time
      with:
        format: YYYYMMDDTHHmm
        utcOffset: "+09:00"

    - name: Generate deployment package
      run: |
        mkdir -p deployment
        cp build/libs/danaga-0.0.1-SNAPSHOT.jar deployment/danaga-0.0.1-SNAPSHOT.jar
        # cp deployment/Procfile deployment/Procfile
        cd deployment && zip -r danaga-${{steps.current-time.outputs.formattedTime}} .
        ls
        
    - name: Deploy Danaga to EB
      uses: einaregilsson/beanstalk-deploy@v14
      with:
        aws_access_key: ${{secrets.AWS_ACCESS_KEY_ID}}
        aws_secret_key: ${{secrets.AWS_SECRET_ACCESS_KEY}}
        application_name: danaga-app
        environment_name: Danaga-env
        version_label: danaga-${{steps.current-time.outputs.formattedTime}}
        region: ap-northeast-1
        deployment_package: deployment/danaga-${{steps.current-time.outputs.formattedTime}}.zip

 

on:
  push:
    branches:
      - master
      

master 브랜치에 push될 때 workflow가 trigger된다.

 

스프링부트3는 자바17이상을 지원하기 때문에 자바 17로 세팅한다.

 

- name: Run chmod to make gradlew executable
      run: chmod +x ./gradlew
      

그냥 빌드를 하려 했더니 gradle 실행 권한이 없어서 권한을 주는 step을 추가한다.

 

 - name: make application.properties
      run: |
          cd ./src/main/resources
          touch ./application.properties
          echo "${{ secrets.APP_PROPERTIES }}" > ./application.properties
     
    - name: make env.properties
      run: |
          cd ./src/main/resources
          touch ./env.properties
          echo "${{ secrets.ENV_PROPERTIES }}" > ./env.properties

 

빌드를 하기 전에 applciation.properties와 env.properties 파일을 생성하고 빌드한다. 

 

 - name: Generate deployment package
      run: |
        mkdir -p deployment
        cp build/libs/danaga-0.0.1-SNAPSHOT.jar deployment/danaga-0.0.1-SNAPSHOT.jar
        # cp deployment/Procfile deployment/Procfile
        cd deployment && zip -r danaga-${{steps.current-time.outputs.formattedTime}} .
        ls
        
    - name: Deploy Danaga to EB
      uses: einaregilsson/beanstalk-deploy@v14
      with:
        aws_access_key: ${{secrets.AWS_ACCESS_KEY_ID}}
        aws_secret_key: ${{secrets.AWS_SECRET_ACCESS_KEY}}
        application_name: danaga-app
        environment_name: Danaga-env
        version_label: danaga-${{steps.current-time.outputs.formattedTime}}
        region: ap-northeast-1
        deployment_package: deployment/danaga-${{steps.current-time.outputs.formattedTime}}.zip
        

 

jar파일을 zip으로 압축하여 ebs에 배포한다. 

 

master에 workflow를 작성하면 트리거가 작동하여 workflow가 실행된다. 

 

배포 step이 실패가 떠서 확인해본다.....

 

 

배포는 성공적으로 했는데 healthcheck가 잘못되어 실패로 처리되었다...

 

로그를 확인하러 aws 콘솔에 갔는데 

상태 ok  

헬스체크의 일시적인 문제로 보고 

 

 

도메인 접속 결과 이상 없이 실행된다. 

 

이렇게 master branch에 push 될 때마다 자동으로 배포가 되는 환경이 구축되었다. 

728x90

 

#무한스크롤

 

기존 페이징 방식에서 벗어나 최근 떠오르는 페이징 방식이다.

 

특정 개수만큼만 로딩하고 특정 뷰포트를 넘어가면 다음 아이템을 로딩하는 방식으로 

비동기 방식으로 html을 누적 생성해낸다. 

 

최근 다양한 sns에서 적용하고 있는 방식이다. 

기존의 페이지네이션은 끝에 도달하면 다음 동작을 해야 다음 컨텐츠를 확인할 수 있는데 

무한 스크롤은 끝에 도달했다고 인지하기도 전에 이미 다음 컨텐츠를 보여주고 있기 때문에 

컨텐츠가 끝없이 쏟아지고 심리적으로 페이지네이션보다 클라이언트를 더 붙잡아두는 경향이 있다. 

 

IntersectionObserver Interface는 

특정 요소를 observe 하면서 해당 요소가 다른 요소와 교차하게 되는 시점을 감지하는 interface이다. 

이 인터페이스를 활용하면 아이템 뷰포트를 옵서브 대상으로 지정하고

뷰포트가 다음 요소와 교차될 때, 혹은 교차되기 이전에 다음 아이템을 렌더링하여 무한 스크롤을 구현할 수 있다.

 

우리가 할 일은 교차를 감지하였을 때 어떤 동작을 할지 콜백함수를 작성해주는 일이다.  

 

#IntersectionObserver 생성자

 

new IntersectionObserver(callback[, options])

 

 

callback(entries, observer) 

콜백함수는 entries와 observer를 매개변수로 받을 수 있다. 

 

observer는 콜백을 호출한 intersectionObserver 객체이다.

 

#intersectionObserverEntry(배열) Properties 

 

- boundingClientRect

타겟 요소의 사각형 바운드

 

- intersectionRect

타켓의 가시 영역

 

- intersectionRatio

boundingClientRect에서 intersectionRect의 교차 비율로 백분율로 표현한다. 

 

- rootBounds

observer의 root 바운드 

 

- isIntersecting

observer의 root를 교차한 상태인지를 boolean 값으로 반환 

 

- target

교차가 발생한 요소

 

- time

교차 시간

 

 

#options

 

- root

 대상 요소의 조상 element

 root의 바운딩을 감지기의 뷰포트로 사용

 

- rootMargin

 교차 영역 계산시 사용할 바운드에 적용할 오프셋 문자열로

 계산할 바운딩 영역을 증가시키거나 감소시킬 수 있다.

 기본값은  "0px 0px 0px 0px"이다. 
 값을 잘못 설정하면 syntax error가 발생한다. 

 

- threshold

 대상의 가시영역과 바운딩 영역의 교차 비율에 대한 역치

0.0~ 1.0 사이의 숫자 값을 갖는다. 

0.0은 root가 가시영역에 조금이라도 보일 때 대상을 볼 수 있는 것으로 감지하고 

1.0은 root가 가시영역에 온전히 보였을 때 대상을 볼 수 있는 것으로 감지한다. 

 

 

threshold를 0.8로 설정하면 

스크롤을 내리다 타겟요소의 0.8을 넘으면 observer가 콜백함수를 호출한다. 

 

threshold의 범위를 초과하여 설정하면 range error가 발생한다. 

 

 

#메소드

 

- disconnect() 

모든 대상의 주시를 해제 

 

- observe()

 주어진 대상 요소를 주시

 

- takeRecords() 

 모든 주시 대상에 대한 entries를 반환

 

- unobserve()

 특정 대상 요소에 대한 주시를 해제

 

https://developer.mozilla.org/ko/docs/Web/API/IntersectionObserver

 

IntersectionObserver - Web API | MDN

Intersection Observer API (en-US)의 IntersectionObserver 인터페이스는 대상 요소와 상위 요소, 또는 대상 요소와 최상위 문서의 뷰포트가 서로 교차하는 영역이 달라지는 경우 이를 비동기적으로 감지할 수

developer.mozilla.org

 

 

 

 

 #구현 예시

 

const $result =$("#toObserve");
const $end = document.createElement("div");
$end.id = 'product-list-observed';
$result.append($end);

const callback = (entries, observer) => {
	entries.forEach((entry) => {
		if (entry.isIntersecting) {
			firstResult += 20;
			filterDto.firstResult = firstResult;
			api.continueSearchResult(filterDto, observer);
		}
	});
}
const options = {
	root: null, // 뷰포트를 기준으로 타켓의 가시성 검사
	rootMargin: '0px 0px 0px 0px', // 확장 또는 축소 X
	threshold: 1 // 타켓의 가시성 0%일 때 옵저버 실행
};
const observer = new IntersectionObserver(callback, options);
observer.observe($end);
Product

{{name}}

{{totalPrice}}원

{{optionSetDesc}}
{{updateTime}}

 

'Javascript' 카테고리의 다른 글

SPA AJAX Request With JQuery  (0) 2023.09.25
js 모듈 의존성 줄이기  (0) 2023.09.24
AJAX using JQuery  (0) 2023.09.24
JQuery event  (0) 2023.09.23
Javascript this  (0) 2023.09.23
123456···31

+ Recent posts