본문 바로가기
Vue.js

2024_08_30_금~09_02_월

by 알케니브 2024. 8. 30.

오늘의 코딩순서

http://localhost:8388/restApi/v1/members

Vue (폴더: vue3Member01) + Spring (폴더: oBootJpaApi01)

  • index.js + NestedView.vue
  • index.js + NestedHomeView.vue + NestedOneView.vue + NestedHomeView.vue +  NestedView.vue
    + application.yml(Spring) 
  • index.js + AppCard.vue + (components에 members 폴더 만듬) MemberItem.vue
    + (views에 members 폴더 만듬) MemberListView.vue
    + (api 폴더 만들고, members 폴더 만듬) members.js
    + WebConfig.class(Spring) 

Docker와 Docker 연결


오늘의 코딩 포인트

Vue (폴더: vue3Member01)

  • index.js
import AboutView from '@/views/AboutView.vue'
import HomeView from '@/views/HomeView.vue'
import NestedView from '@/views/nested/NestedView.vue'
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'About',
    component: AboutView
  },
  {
    path: '/nested',
    name: 'Nested',
    component: NestedView
  }
]

const router = createRouter({
  history: createWebHistory('/'),
  routes: routes
})

export default router
  • NestedView.vue
<template>
  <div>
    <h1>Nested View Welcome!!!!</h1>
  </div>
</template>

<script setup></script>

<style lang="scss" scoped></style>


  • index.js
import AboutView from '@/views/AboutView.vue'
import HomeView from '@/views/HomeView.vue'
import NestedHomeView from '@/views/nested/NestedHomeView.vue'
import NestedOneView from '@/views/nested/NestedOneView.vue'
import NestedTwoView from '@/views/nested/NestedTwoView.vue'
import NestedView from '@/views/nested/NestedView.vue'
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'About',
    component: AboutView
  },
  {
    path: '/nested',
    name: 'Nested',
    component: NestedView,
    children: [
      {
        path: '',
        name: 'NestedHome',
        component: NestedHomeView
      },
      {
        path: 'one',
        name: 'NestedOne',
        component: NestedOneView
      },
      {
        path: 'two',
        name: 'NestedTwo',
        component: NestedTwoView
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory('/'),
  routes: routes
})

export default router

 

  • NestedHomeView.vue
<template>
  <div class="card">
    <div class="card-header">Nested Home</div>
    <div class="card-body">
      <h1 class="card-title">Nested routes home...</h1>
    </div>
  </div>
</template>

<script setup></script>

<style lang="scss" scoped></style>

 

  • NestedOneView.vue
<template>
  <div class="card">
    <div class="card-header">Nested One</div>
    <div class="card-body">
      <h5 class="card-title">Special 1</h5>
      <p class="card-text">One View예요</p>
      <a href="#" class="btn btn-danger">Go danger</a>
    </div>
  </div>
</template>

<script setup></script>

<style lang="scss" scoped></style>

 

  • NestedTwoView.vue
<template>
  <div class="card">
    <div class="card-header">Nested Two</div>
    <div class="card-body">
      <h5 class="card-title">Special 2</h5>
      <p class="card-text">Two View예요</p>
      <a href="#" class="btn btn-danger">Go Two danger</a>
    </div>
  </div>
</template>

<script setup></script>

<style lang="scss" scoped></style>

 

  • NestedView.vue
<template>
  <div>
    <ul class="nav nav-pills">
      <li class="nav-item">
        <RouterLink
          class="nav-link"
          active-class="active"
          :to="{ name: 'NestedOne', replace: true }"
        >
          <!-- NestedOneView를 받아서, RouterLink를 타고 NestedView로 전달해서 화면에 나타냄 -->
          Nested One
        </RouterLink>
      </li>
      <li class="nav-item">
        <RouterLink
          class="nav-link"
          active-class="active"
          :to="{ name: 'NestedTwo', replace: true }"
        >
          Nested Two
        </RouterLink>
      </li>
    </ul>
    <hr />
    <!-- ul안의 내용이 RouterView를 통해 나타남 -->
    <RouterView></RouterView>
  </div>
</template>

<script setup></script>

<style lang="scss" scoped></style>

 

 

  • application.yml (Spring) 
    Tip)
    • 1521: 나의 DB포트를 이용하겠다는 뜻
server:
  port: 8388
# Oracle Connect
spring:
  datasource:
    driver-class-name: oracle.jdbc.OracleDriver
    url: jdbc:oracle:thin:@172.30.1.9:1521/xe # docker(mydb)
    #url: jdbc:oracle:thin:@localhost:1521/xe
    username: scottJpa
    password: tiger
    
  #JPA Setting
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update

#logger을 통해 hibernate를 실행하는 SQL      
logging.level:
  org.hibernate.SQL: debug

 

http://localhost:8388/restApi/v1/members


  • index.js
import AboutView from '@/views/AboutView.vue'
import HomeView from '@/views/HomeView.vue'
import MemberListView from '@/views/members/MemberListView.vue'
import NestedHomeView from '@/views/nested/NestedHomeView.vue'
import NestedOneView from '@/views/nested/NestedOneView.vue'
import NestedTwoView from '@/views/nested/NestedTwoView.vue'
import NestedView from '@/views/nested/NestedView.vue'
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'About',
    component: AboutView
  },
  {
    path: '/members',
    name: 'MemberList',
    component: MemberListView
  },
  {
    path: '/nested',
    name: 'Nested',
    component: NestedView,
    children: [
      {
        path: '',
        name: 'NestedHome',
        component: NestedHomeView
      },
      {
        path: 'one',
        name: 'NestedOne',
        component: NestedOneView
      },
      {
        path: 'two',
        name: 'NestedTwo',
        component: NestedTwoView
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory('/'),
  routes: routes
})

export default router
  • AppCard.vue
    Tip) https://velog.io/@doheek2/Vue-%EC%8A%AC%EB%A1%AFSlot 참고
    • slot
      • 템플릿 조각을 자식 컴포넌트에 전달하여 자식 컴포넌트가 자체 템플릿 내에서 조각을 렌더링하는 것 
      • v-slot은 분해 할당하여 사용할 수 있음 
      • 이름이 있고 범위가 지정된 슬롯은 아래와 같이 사용할 수 있음 
      • 명명된 슬롯과 기본 범위 슬롯을 혼합하여 컴포넌트에 v-slot 지시어를 사용하면 컴파일 에러가 발생함
      • 기본 슬롯에 명시적인 <template>을 사용하여 v-slot 사용을 피해 다른 슬롯에서 message prop을 사용할 수 없음을 명확히 알 수 있음 
        1. 이름 있는 슬롯
          • name이라는 속성으로 컨텐츠가 렌더링되어야 하는 위치를 결정할 수 있음
          • name 속성이 없다면 기본적으로 default란 이름을 가짐
          • 컴포넌트가 기본 슬롯과 이름이 있는 슬롯을 모두 허용하는 경우, 최상위 비 <template> 노드는 기본 슬롯의 컨텐츠로 처리됨
        2. 동적인 슬롯  이름: 동적인 디렉티브 인자는 v-slot에서도 작동함
        3. 범위가 지정된 슬롯
          • 슬롯 컨텐츠는 부모 컴포넌트에 정의되어 있으므로 부모 컴포넌트의 데이터 범위에 접근할 수 있음
          • 상위 컴포넌트에서 선언된 슬롯 컨텐츠에서 자식 컴포넌트(하위) 범위 내에 있는 데이터를 사용할 수 없음
          •  자식 컴포넌트(하위) 범위 내에 있는 데이터를 상위 컴포넌트에서 선언된 슬롯 컨텐츠에서 사용할 수 있음
            1. <slot :text="greetingMsg" :count="count" /> :  속성을 부모 컴포넌트에 작성된 슬롯 컨텐츠에 전달
            2. <MyComponent v-slot="slotProps">: 단일 기본 슬롯을 사용할 때와 달리 v-slot을 통해 슬롯 props를 받음
            3. <p>{{ slotProps.text }} {{ slotProps.count }}</p> : 슬롯 범위 내에서 슬롯 props를 통해 값을 사용할 수 있음
<template>
  <div class="card">
    <div v-if="$slots.header" class="card-header">
      <slot name="header"></slot>
      <!-- header에 값을 뿌려줌 -->
    </div>
    <div v-if="$slots.default" class="card-body">
      <!-- body에 값을 뿌려줌 -->
      <slot></slot>
    </div>
    <div class="card-footer">
      <slot name="footer"></slot>
      <!-- footer에 값을 뿌려줌 -->
    </div>
  </div>
</template>

 

  • MemberItem.vue
<template>
  <!-- 자식 선언 -->
  <AppCard>
    <template v-slot:header>회원번호</template>
        <!-- slot에 지정을 따로 해주지 않으면 body라고 칭함 
     AppCard의 default값이 여기에 들어감-->
    <h5 class="card-title">{{ id }}</h5>
    <p class="card-text">{{ name }}</p>
    <p class="text-muted">{{ sal }}</p>
    <template v-slot:footer>{{ name }}말씀</template>
  </AppCard>
</template>

<!-- setup를 사용하면 return을 쓰지 않아도 알아서 값이 입력됨 -->
<script setup>
import AppCard from '@/components/AppCard.vue'

defineProps({
  id: {
    type: Number,
    required: true
  },
  name: {
    type: String
  },
  sal: {
    type: [String, Number]
    // []배열로 표기하면 하나 이상의 타입을 넣을 수 있음
  }
})
</script>

<style lang="scss" scoped></style>

 

  • MemberListView.vue
    Tip)
    • async-await: vue에서 ajax처럼 호출하는 방법
<template>
  <div>
    <h2>회원 목록</h2>
    <hr />
    <div class="row g-3">
      <div v-for="member in members" :key="member.id" class="col-3">
       <!-- g-다음의 숫자만큼 카드의 간격을 조절할 수 있음 -->
        <!-- 위의 members를 아래에 전달 -->
        <!-- col-다음의 숫자를 6으로 나눈 수만큼 가로에 보여줌
        <MemberItem
          :id="member.id"
          :name="member.name"
          :sal="member.sal"
          @click="goMemberDetail(member.id)"
        >
        </MemberItem>
      </div>
    </div>
  </div>
</template>

<script setup>
import { getMembers } from '@/api/members'
import MemberItem from '@/components/members/MemberItem.vue'
import { ref } from 'vue'
// import { useRouter } from 'vue-router'

// const router = useRouter()
const members = ref([])
const fetchMembers = async () => {
  // async: 비동기
  try {
    const { data } = await getMembers()
    // members.js와 연결
    members.value = data
    // totalCount.value = headers['x-total-count'];
    console.log('members.value2->', members.value)
  } catch (error) {
    console.error(error)
  }
}
fetchMembers()
// 한번은 실행하도록 하기
</script>

<style lang="scss" scoped></style>

 

  • members.js
import axios from 'axios'

export function getMembers() {
  //MemberListView.vue의 getMembers와 연결됨
  console.log('getMembers Start...')
  return axios.get('http://localhost:8388/restApi/v1/members') // Risk API
}
  • WebConfig.class (Spring) 
    Tip)
    • CORS는 한 도메인이 도메인 간의 요청을 가진 다른 도메인의 리소스에 액세스할 수 있게 해주는 보안 메커니즘으로 최신 브라우저에서 구현된 동일 출처 정책 때문에 등장함
package com.oracle.oBootJpa01;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// CORS는 한 도메인이 도메인 간의 요청을 가진 다른 도메인의 리소스에 
//액세스할 수 있게 해주는 보안 메커니즘으로 
//최신 브라우저에서 구현된 동일 출처 정책 때문에 등장

@Configuration
public class WebConfig implements WebMvcConfigurer {
	
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		// WebMvcConfigurer.super.addCorsMapping(registry);
		registry.addMapping("/**")
				.allowedOrigins("*")
				.allowedMethods("GET", "POST" ,"PUT")
				.maxAge(3000);
		
		
	}

}

 

도커까지 실행한 후의 메인 페이지↓↓↓


질문목록

 


수업교재

 

 


오늘의 숙제

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

2024_09_04_수~09_04_목  (0) 2024.09.04
2024_09_03_화  (0) 2024.09.03
2024_08_29_목  (0) 2024.08.28
2024_08_28_수  (0) 2024.08.28
2024_08_27_화  (0) 2024.08.27