728x90
반응형 데이터를 만들기 위한 함수

ref

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

let id = 0

const newTodo = ref('')
const hideCompleted = ref(false)
const todos = ref([
  { id: id++, text: 'HTML 배우기', done: true },
  { id: id++, text: 'JavaScript 배우기', done: true },
  { id: id++, text: 'Vue 배우기', done: false }
])
  • 사용 대상: 모든 종류의 값 (기본 데이터 타입 또는 객체/배열).
  • 접근 방법: .value 속성을 통해 값에 접근하고 변경합니다.
  • 주요 특징: 기본 데이터 타입(예: 문자열, 숫자, 불리언)에 반응성을 부여할 수 있으며, 객체나 배열에도 사용 가능하지만, 단일 값에 대한 반응성을 관리하는 데 주로 사용됩니다.

reactive

<script setup>
import { reactive } from 'vue'

let id = 0

const state = reactive({
  newTodo: '',
  hideCompleted: false,
  todos: [
    { id: id++, text: 'HTML 배우기', done: true },
    { id: id++, text: 'JavaScript 배우기', done: true },
    { id: id++, text: 'Vue 배우기', done: false }
  ]
})
</script>
  • 사용 대상: 객체나 배열.
  • 접근 방법: .value 속성 없이 객체나 배열 내부의 속성에 직접 접근합니다.
  • 주요 특징: 객체나 배열 전체에 대한 반응성을 부여합니다. 중첩된 객체나 배열을 포함하여 구조 내의 모든 데이터에 반응성이 적용됩니다.

 

reactive 사용의 이점

  1. 직관적인 접근과 수정: reactive 객체의 속성에 접근하거나 수정할 때, .value를 사용할 필요가 없습니다. 이는 코드를 좀 더 직관적으로 만들고, 일반 JavaScript 객체와 동일한 방식으로 작업할 수 있게 합니다.
  2. 중첩된 반응성: reactive를 사용하면, 객체 내의 중첩된 속성도 자동으로 반응성이 적용됩니다. 따라서 객체 내부 구조가 복잡해지거나 중첩된 경우에도, 별도의 처리 없이 모든 레벨에서 반응성을 유지할 수 있습니다.
  3. 코드 조직: 여러 반응형 데이터를 하나의 reactive 객체에 그룹화하여 관리할 수 있습니다. 이는 관련 데이터와 로직을 함께 묶어서 관리하기 편리하게 해줍니다, 코드의 가독성과 유지보수성을 향상시킬 수 있습니다.

선택 기준

  • 기본 타입(문자열, 숫자, 불리언 등)에 대한 반응성이 필요한 경우, 또는 Vue 템플릿에서 직접 참조되는 단일 변수에 대해서는 ref를 사용하는 것이 좋습니다.
  • 객체나 배열과 같은 복합적인 데이터 구조에 대해 반응성을 제공하고, 중첩된 데이터 구조를 다루거나 여러 관련 데이터를 함께 관리해야 하는 경우 reactive를 사용하는 것이 더 적합할 수 있습니다.
728x90

전통적인 Options API

 

Vue에서의 스크립트 구현은 크게 두가지 방식이 있다. 전통적인 Options API와 Composition API이다.

전통적인 Options API

<template>
  <div>
    <input v-model="newTodo" @keyup.enter="addTodo">
    <ul>
      <li v-for="todo in todos" :key="todo.id">
        {{ todo.text }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      newTodo: '',
      todos: [],
      nextId: 0
    }
  },
  methods: {
    addTodo() {
      this.todos.push({ id: this.nextId++, text: this.newTodo });
      this.newTodo = ''; // 입력 필드 초기화
    }
  }
}
</script>

Composition API

Vue 3의 Composition API는 컴포넌트 로직을 구성하는 새로운 방법을 제공합니다. 이 API의 도입은 Vue 애플리케이션을 설계하고 개발하는 방식에 근본적인 변화를 가져왔습니다. Composition API의 특징과 장점은 다음과 같습니다:

특징

  1. 반응형 상태 정의: refreactive 함수를 사용하여 반응형 상태를 선언적으로 정의할 수 있습니다. 이 상태들은 컴포넌트의 템플릿에서 직접 사용될 수 있으며, 상태 변경 시 자동으로 화면을 업데이트합니다.
  2. setup 함수: 컴포넌트의 반응형 상태, 계산된 속성, 메서드 등 모든 로직을 setup 함수 내에 정의합니다. setup 함수는 컴포넌트의 옵션/라이프사이클 훅보다 먼저 실행되며, 여기에서 정의된 모든 값과 함수는 템플릿에서 직접 사용할 수 있습니다.
  3. 함수 기반의 구성: 로직을 재사용 가능한 함수로 캡슐화하고 조합하여 컴포넌트를 구성할 수 있습니다. 이러한 접근 방식은 컴포넌트 간에 코드를 재사용하고 조합하기 쉽게 만듭니다.

장점

  1. 로직의 재사용 및 조직화: Composition API를 사용하면 로직을 기능별로 구성하고 필요한 곳에서 재사용할 수 있습니다. 이는 코드의 중복을 줄이고, 프로젝트의 유지보수성을 향상시킵니다.
  2. 유연성: 컴포넌트의 로직을 더 유연하게 조직할 수 있습니다. 예를 들어, 관련된 모든 반응형 상태와 함수를 함께 그룹화하여 관리할 수 있으며, 이는 컴포넌트의 가독성을 크게 향상시킵니다.
  3. 타입스크립트와의 호환성: Composition API는 타입스크립트와 더욱 잘 호환됩니다. setup 함수 내에서 정의된 상태와 함수에 타입을 적용하기 쉬워, 타입스크립트를 사용하는 프로젝트에서 타입 안정성과 개발 경험을 향상시킬 수 있습니다.
  4. 응집력 있는 코드 베이스: 기능별로 로직을 그룹화하고 재사용할 수 있기 때문에, 관련 로직이 물리적으로 가까운 위치에 있게 됩니다. 이는 응집력 있는 코드 베이스를 유지하는 데 도움이 됩니다.
  5. 더 나은 성능: Composition API를 사용하면 필요한 로직만을 정확히 구성하여 사용할 수 있기 때문에, 불필요한 로직의 실행을 최소화할 수 있습니다. 이는 애플리케이션의 성능을 최적화하는 데 기여할 수 있습니다.

Composition API는 Vue 애플리케이션을 개발할 때 더 큰 유연성과 향상된 코드 재사용성을 제공합니다. 이는 특히 대규모 애플리케이션 또는 복잡한 컴포넌트 구조를 가진 프로젝

트에서 그 장점이 두드러집니다.

Vue 3 초기 버전에서의 Composition API 사용 예시

<template>
  <div>
    <input v-model="newTodo" @keyup.enter="addTodo" />
    <ul>
      <li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li>
    </ul>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const newTodo = ref('');
    const todos = ref([]);

    const addTodo = () => {
      todos.value.push({ id: todos.value.length + 1, text: newTodo.value });
      newTodo.value = '';
    };

    return { newTodo, todos, addTodo };
  }
};
</script>

Vue 3 초기 버전에서 Composition API를 사용할 때는 setup 함수 내에서 컴포넌트의 반응형 상태, 계산된 속성, 메서드 등을 정의하고, 필요한 값들을 객체로 반환하여 템플릿에서 사용할 수 있도록 했습니다.

 

Vue 3.2 이후에서의 <script setup> 구문 사용 예시

Vue 3.2부터 도입된 <script setup> 구문을 사용하면, setup 함수 내에서 반환 과정 없이 직접 변수와 함수를 정의할 수 있으며, 이들은 자동으로 템플릿에서 사용 가능해집니다. 이로 인해 코드가 더욱 간결하고 선언적으로 변화했습니다.

 

<template>
  <div>
    <input v-model="newTodo" @keyup.enter="addTodo" />
    <ul>
      <li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const newTodo = ref('');
const todos = ref([]);

const addTodo = () => {
  todos.value.push({ id: todos.value.length + 1, text: newTodo.value });
  newTodo.value = '';
};
</script>

주요 차이점

  • 코드 간결성: <script setup> 구문을 사용하면, 컴포넌트의 로직을 더욱 간결하게 작성할 수 있습니다. setup 함수 내에서 반환 과정 없이 모든 로직을 정의할 수 있으며, 이는 코드의 가독성과 유지보수성을 크게 향상시킵니다.
  • 자동 import 추론: <script setup>은 IDE와 도구에서 더 나은 타입 추론과 자동 완성을 제공합니다. 또한, Vue 컴파일러는 <script setup> 내에서 사용된 Vue API를 자동으로 감지하여 필요한 코드를 자동으로 import 합니다.
  • 컴포넌트 정의의 선언적 표현: <script setup> 구문은 컴포넌트의 정의를 더 선언적으로 만들어 줍니다. 이는 Vue 컴포넌트의 구조와 로직을 더 명확하게 표현할 수 있게 해줍니다.

<script setup> 구문은 Vue 3.2 이상에서 Composition API를 사용하는 새로운 표준이 되었으며, 많은 개발자들이 이 새로운 패턴을 선호하고 있습니다.

728x90

콜백 함수 (Callback Function)

  • 정의: 콜백 함수는 다른 함수에 인자로 전달되는 함수입니다. 이 함수는 외부 함수의 내부 로직에 의해 특정 시점에 호출됩니다. 콜백 함수는 비동기 처리, 이벤트 리스너 등록, 고차 함수(higher-order function)에서 자주 사용됩니다.
  • 용도: 콜백 함수는 비동기 작업의 결과를 처리하거나, 특정 이벤트가 발생했을 때 수행할 작업을 정의하는 데 사용됩니다. 예를 들어, 서버로부터 데이터를 요청하고 그 데이터가 도착했을 때 실행될 함수, 또는 사용자가 버튼을 클릭했을 때 실행될 함수 등이 콜백 함수로 사용될 수 있습니다.
  • 예시:
// setTimeout 함수에 전달된 익명 함수는 콜백 함수입니다.
setTimeout(() => {
    console.log("1초 후에 실행됩니다.");
}, 1000);

핸들러 (Handler)

  • 정의: 핸들러는 특정 이벤트나 조건에 대응하여 실행되도록 지정된 함수를 의미합니다. 이 용어는 주로 이벤트 처리와 관련된 맥락에서 사용됩니다.
  • 용도: 이벤트 핸들러는 사용자의 입력, 시스템 이벤트, 네트워크 응답 등 다양한 이벤트에 반응하기 위해 사용됩니다. 웹 개발에서는 DOM 이벤트(클릭, 키보드 입력 등)를 처리하는 함수를 이벤트 핸들러라고 부릅니다.
  • 예시:
<!-- HTML에서 버튼 클릭 이벤트에 대한 핸들러를 정의합니다. -->
<button onclick="alert('클릭됨!')">클릭하세요</button>
// JavaScript에서 이벤트 리스너를 추가하는 방식으로 핸들러를 지정할 수 있습니다.
document.getElementById("myButton").addEventListener("click", function() {
    alert("버튼이 클릭되었습니다.");
});

결론

요약하자면, 콜백 함수는 넓은 의미에서 다른 함수의 실행 완료, 비동기 작업의 완료 등 다양한 상황에서 호출되도록 예정된 모든 함수를 말하며, 핸들러는 특히 이벤트 처리와 관련된 맥락에서 사용되는 용어입니다. 따라서 모든 이벤트 핸들러는 콜백 함수의 한 형태라고 볼 수 있지만, 모든 콜백 함수가 이벤트 핸들러는 아닙니다.

'Mobile App Develop' 카테고리의 다른 글

UDK 3 관련 자료, 자습서 링크  (0) 2020.12.15
728x90
Proxy : 대리

 

Proxy는 JavaScript에서 객체의 기본 동작(예: 속성 접근, 할당, 열거, 함수 호출 등)을 사용자 정의 동작으로 가로채서(trap) 조작할 수 있게 해주는 기능입니다.

즉, 객체에 대한 작업을 가로채서 그 작업이 수행되기 전, 수행된 후, 또는 작업을 완전히 대체하여 추가적인 로직을 실행할 수 있게 해줍니다. 이런 특징 때문에 Proxy는 다양한 고급 작업과 패턴에 유용하게 사용될 수 있습니다.

 

const target = {
  message1: "hello",
  message2: "everyone",
};

const handler1 = {};

const proxy1 = new Proxy(target, handler1);

proxy1.message1 = 'hi';
console.log('target', target);

위의 코드에서 target의 message1 속성의 값은 hi로 변경되게 된다.

 

아래 예제를 통해 Proxy로 할 수 있는 다양한 가능성을 살펴보겠다.

1. 속성 읽기 접근 제어

속성을 읽을 때마다 로그를 출력하도록 합니다. get은 속성값을 가져오기 위한 trap(가로채기)이다.

(...)
const handler1 = {
  get(target, prop, receiver) {
    console.log(`속성 '${prop}'에 접근함`);
    return Reflect.get(...arguments);
  }
};

const proxy1 = new Proxy(target, handler1);

console.log(proxy1.message1); // 속성 'message1'에 접근함 -> hello
console.log(proxy1.message2); // 속성 'message2'에 접근함 -> everyone

Proxy는 반드시 new 키워드로만 생성가능하다.

 

2. 속성 쓰기 접근 제어

속성에 값을 할당할 때 유효성 검사를 추가합니다. 예를 들어, message1에는 오직 문자열만 할당될 수 있게 합니다.

(...)
const handler1 = {
    set(target, prop, value) {
        if (prop === "message1" && typeof value !== "string") {
            throw new Error("message1은 문자열이어야 합니다.");
        }
        target[prop] = value;
        return true; // 성공적으로 값을 할당했음을 나타냄
    }
};

const proxy1 = new Proxy(target, handler1);

proxy1.message1 = "안녕하세요"; // 성공
try {
    proxy1.message1 = 123; // Error: message1은 문자열이어야 합니다.
} catch (e) {
    console.error(e.message);
}

 

3. 속성 존재 여부 확인

객체에 특정 속성이 있는지 확인할 때마다 로그를 출력합니다.

const handler1 = {
  has(target, prop) {
    console.log(`${prop} 속성이 객체에 존재하는지 확인`);
    return prop in target;
  }
};

const proxy1 = new Proxy(target, handler1);

console.log('message1' in proxy1); // message1 속성이 객체에 존재하는지 확인 -> true
console.log('message3' in proxy1); // message3 속성이 객체에 존재하는지 확인 -> false

 

이외에도 다양한 Handler Function을 통해 객체의 기본동작을 정의하고 유연하게 사용할 수 있다.

 

Proxy생성자와 Handler Function에 대해서는 다음 문서를 참고

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy

 

Proxy() constructor - JavaScript | MDN

The Proxy() constructor creates Proxy objects.

developer.mozilla.org

'Study > JavaScript' 카테고리의 다른 글

html + javascript 기초  (0) 2021.01.07
자바스크립트 - 의사 결정  (0) 2017.03.28
자바 스크립트 기초 - 함수, 메서드, 객체  (0) 2017.03.27
728x90

AWS 리눅스 환경에 CI & CD를 적용하는데 

액션러너 설정에서 다음과 같은 오류가 나며 진행이 되지 않았다.

 

Libicu's dependencies is missing for Dotnet Core 6.0
Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 6.0 dependencies.

기본적으로 AWS Linuxt는 X64 아키텍처라곤 하나 위에서 알려주는 .sh 파일 실행만으론 의존성이 해결되지 않는다.

종속성 문제이기 때문에 해결법은 필요한 라이브러리를 따로 설치해주면 된다.

 

sudo yum install perl-Digest-SHA -y

sudo yum install libicu -y

 

이후에 ./config.sh를 실행하면 정상적으로 GitHub의 액션런너와 연결이 되고 설정과정을 진행할 수 있게 된다.

728x90

백엔드에서 이미지 파일을 static 폴더에 올렸다면 일반적으로는 프론트에서 참조할 수가 없다.

하지만 express는 이미지 파일과 같은 정적 파일들을 쉽게 참조하여 반환할 수 있는 메소드를 제공한다.

 

| Back-end

app.use('/static', express.static(path.join(__dirname, 'static')));

express.static 메소드는 node프로세스가 실행되는 path에 따라 상대적이므로 위와 같이 환경변수인 __dirname을 써서 디렉토리명을 지정해줄 수 있다. node 프로세스가 실행되는 같은 경로의 static 폴더를 참조한다.

 

그러면 아래와 같이 참조했을 때 백엔드의 static 디렉토리내의 모든 파일을 참조할 수 있게 된다.

http://localhost:3000/api/static/images/mt/resize-1697954077172.jpg

 

| Front-end

다음은 nuxt.config.js를 수정하여

api라는 경로로 들어왔을 때 경로를 다시 쓰기해주도록 한다.

 axios: {
        proxy: true,
        prefix: '/api'
    },

    proxy: {
        '/api': { target: process.env.AXIOS_BASE_URL, pathRewrite: { '^/api/': '/' } },
    },

[프론트 .env 폴더에 정의된 api 주소]

AXIOS_BASE_URL=http://127.0.0.1:10072

 

만약 DB에 저장된 path과 다음과 같다면 프론트의 메소드나 computed 속성으로 경로를 반환하면 될 것이다.

static/images/mt/resize-1697954077178.jpg

 

  getImageUrl(img) {
            if (img.path && img.path.includes('static/images')) {
                return '/api/' + img.path;
            }
            return URL.createObjectURL(img);
        },

그러면 백엔드에서 굳이 파일객체를 리턴한다는지 리소스가 많이들고 비효율적인 방법을 쓰지않고도 쉽게 리소스를 참조할 수 있게 된다.

728x90

Postman에 직접 컬렉션을 만들고 API 요청을 작성할 수도 있지만

다른 유저가 작성한 것을 가져와 사용할 수도 있다.

 

https://www.postman.com/explore

 

Postman | Postman API Network

Browse the largest network of APIs, workspaces, and collections by developers across the planet

www.postman.com

위의 URL에서 검색하거나

 

postman 데스크탑용 프로그램에서 아래와 같이 다른유저가 작성한 공개 컬렉션을 검색가능하다.

공개된 컬렉션 이름을 누르면 아래와 같이 자세한 요청 명세를 확인가능하다.

컬렉션 이름을 마우스 우클릭하면 아래에 Export가 보인다. 이를 클릭하면 json형태의 파일로 내려받을 수 있다.

이제 My workspace에서 import버튼을 누르면 아래와 같은 창이 뜨는데 해당 파일을 드래그앤 드랍해주면 컬렉션이 추가된다.

 

 

PROFIT!

힘들여 작성하지 말고 검색해서 가져오자!

728x90

Nuxt.js / Node.js로 구성된 프로젝트에서 다음과 같은 에러가 뜨고 백엔드 API 요청이 되지 않는 현상이 있었다.

Error occurred while trying to proxy request

 

결론적으로 아래 환경 변수 (프론트의 .env)에서 localhost를 127.0.0.1 로 바꾸니 해결되었다.

AXIOS_BASE_URL=http://localhost:8071

m1 mac과 node18 버전에서 생기는 호환성 문제라고 하는데 이것도 환경마다 차이가 있어서 정확한 원인을 알 수가 없다.

아시는 분은 댓글 달아주세요~!

 

| 참고

https://github.com/chimurai/http-proxy-middleware/issues/171

728x90

Settings > Developer Settings

메뉴에서

 

Personal access tokens > Tokens (elascic) 을 선택

Generate new token

 

git pull과 action을 사용하기 위해선 workflow만 선택하면 된다.

 

실서버에서 다음과 같이 pull을 받으면 된다.

git pull https://<계정명>:ghp_test6efghNantoKaNantoKaKUtYa1234Vx3Y0@github.com/test/test.git <branch이름>

 

728x90

Template

 <tr
    v-for="(diary, idx) in f_rec_diary_list"
    :key="idx"
    draggable="true"
    @dragstart="drag_area(idx, 'M')"
    @dragend="drag_end_area"
    @dragover.prevent
    @dragenter="drag_enter_area(idx)"
    @drop="drop_area(idx, f_rec_diary_list)"
>

 

Data

data() {
        return {            
            dragged_area_index: null,
            hover_area_index: null,
            dragged_item_index: null,
            hover_item_index: null,
            dragged_type: null,
        };
},

 

Methods

drag_area(index, type) {
    console.log("drag_area -> index", index, "type", type);
    this.dragged_area_index = index;
    this.dragged_type = type;
},
drag_enter_area(index) {
    this.hover_area_index = index;
},
drag_end_area() {
    this.hover_area_index = null;
},
async drop_area(drop_index, parent) {
    if (this.dragged_type !== "M") {
        return;
    }

    const dragged_item = parent.splice(this.dragged_area_index, 1)[0];
    parent.splice(drop_index, 0, dragged_item);

    console.log('parent', parent);

    // Update seq_no values after reordering
    for (let i = 0; i < parent.length; i++) {
        parent[i].seq_no = i + 1;
    }

    console.log("dragged_item", dragged_item);

    this.dragged_area_index = null;
    this.hover_area_index = null;
    this.dragged_type = null;
},
drag_item(index, type) {
    this.dragged_item_index = index;
    this.dragged_type = type;
},
drag_enter_item(index) {
    this.hover_item_index = index;
},
drag_end_item() {
    this.hover_item_index = null;
},

+ Recent posts