오늘의 코딩순서
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을 사용할 수 없음을 명확히 알 수 있음
- 이름이 있는 슬롯
- name이라는 속성으로 컨텐츠가 렌더링되어야 하는 위치를 결정할 수 있음
- name 속성이 없다면 기본적으로 default란 이름을 가짐
- 컴포넌트가 기본 슬롯과 이름이 있는 슬롯을 모두 허용하는 경우, 최상위 비 <template> 노드는 기본 슬롯의 컨텐츠로 처리됨
- 동적인 슬롯 이름: 동적인 디렉티브 인자는 v-slot에서도 작동함
- 범위가 지정된 슬롯
- 슬롯 컨텐츠는 부모 컴포넌트에 정의되어 있으므로 부모 컴포넌트의 데이터 범위에 접근할 수 있음
- 상위 컴포넌트에서 선언된 슬롯 컨텐츠에서 자식 컴포넌트(하위) 범위 내에 있는 데이터를 사용할 수 없음
- 자식 컴포넌트(하위) 범위 내에 있는 데이터를 상위 컴포넌트에서 선언된 슬롯 컨텐츠에서 사용할 수 있음
- <slot :text="greetingMsg" :count="count" /> : 속성을 부모 컴포넌트에 작성된 슬롯 컨텐츠에 전달
- <MyComponent v-slot="slotProps">: 단일 기본 슬롯을 사용할 때와 달리 v-slot을 통해 슬롯 props를 받음
- <p>{{ slotProps.text }} {{ slotProps.count }}</p> : 슬롯 범위 내에서 슬롯 props를 통해 값을 사용할 수 있음
- 이름이 있는 슬롯
- slot
<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 |