반응형

Video 출력

User의 기기에 있는 디바이스에 접근하기 위해 navigator.mediaDevices.getUserMedia를 사용해야한다.

유저의 미디어를 string으로 전해 줄 것이다. 

이건 async function으로 해야한다.

//constraints는 기본적으로 우리가 무엇을 더고 싶은지 작성해야한다.
async function getMedia(constraints) {
  try {
    stream = await navigator.mediaDevices.getUserMedia({
    //constraints
    audio:true,
    video:true,
    });
    myFace.srcObject = stream;
  } catch (e) {
    console.log(e);
  }
}

 

Video 동작 

이렇게 stream을 가져오게 되면 우리에게 track이라는 것을 제공해줄것이다. 우리는 해당 Track에 접근할 수 있다.

function handleMute() {
  stream.getAudioTracks().forEach((each) => (each.enabled = !each.enabled));
  if (!muteBtn) {
    muteBtn = true;
    micBtn.innerText = "MIC MUTE";
  } else {
    muteBtn = false;
    micBtn.innerText = "MIC UNMUTE";
  }
}
function handleVideoOnOff() {
  stream.getVideoTracks().forEach((each) => (each.enabled = !each.enabled));
  if (!videoOnOff) {
    videoOnOff = true;
    videoBtn.innerText = "VIDEO OFF";
  } else {
    videoOnOff = false;
    videoBtn.innerText = "VIDEO ON";
  }
}

 

또한 설치되어 있는 카메라들에게도 접근이 가능하다.

enumerateDevices는 모든 장치와 미디어장치를 알려준다.

장치를 가져오고 select element를 통한 이벤트를 주어서 select의 value가 변할 때  장치의 화면이 바뀌게 코드를 수정한다.

이 때 getMedia에 인자를 주어 코드를 수정한다.

async function findDevices() {
  try {
    const myAllDevice = await navigator.mediaDevices.enumerateDevices();
    const myCamDevice = myAllDevice.filter(
      (device) => device.kind === "videoinput"
    );
    const currentCam = stream.getVideoTracks()[0];
    camSelect.innerHTML = "";
    myCamDevice.forEach((each) => {
      const option = document.createElement("option");
      option.value = each.deviceId;
      option.innerText = each.label;
      if (currentCam.label === each.label) {
        option.selected = true;
      }
      camSelect.appendChild(option);
    });
  } catch (e) {
    console.log(e);
  }
}

async function handleCamChange() {
  await getMedia(camSelect.value);
}

async function getMedia(constraints) {
  const basicConstraints = {
    audio: true,
    video: {facingMode: "user"},
  };
  const mutateConstraints = {
    audio: true,
    video: {
      deviceId: constraints,
    },
  };
  try {
    stream = await navigator.mediaDevices.getUserMedia(
      !constraints ? basicConstraints : mutateConstraints
    );

    if (!constraints) {
      await findDevices();
    }
    myFace.srcObject = stream;
  } catch (e) {
    console.log(e);
  }
}

 

 

728x90

'SocketIO & WebRTC > WebRTC' 카테고리의 다른 글

WebRTC (ICE Candidate / Senders / Stun)__003  (0) 2023.11.24
WebRTC (SocketIO / Offer / Answer )__002  (0) 2023.11.23
반응형

Data Fetch 

Nuxt의 문서에서 Data Fetch와 관련한 문서를 보면$fetch /  useAsyncData / useFetch 로 나누어져 있다. 

<script setup lang="ts">
// SSR 동안 데이터는 서버에서 한 번, 클라이언트에서 한 번, 총 두 번 가져옵니다.
const dataTwice = await $fetch('/api/item')

// SSR 중에는 데이터가 서버 측에서만 가져와 클라이언트로 전송됩니다.
const { data } = await useAsyncData('item', () => $fetch('/api/item'))

//useAsyncData + $fetch의 바로가기로 Fetch를 사용할 수도 있습니다.
const { data } = await useFetch('/api/item')
</script>

 

$fetch

(https://nuxt.com/docs/api/utils/dollarfetch)

$fetch는  클라이언트 측에서만 실행되는 모든 메소드에 사용할 수 있습니다 .

또한 useAsyncData로 래핑하지 않고 구성 요소에서 $fetch를 사용하면 데이터를 두 번 가져오게 됩니다.

처음에는 서버에서, 그 다음에는 하이드레이션 중에 클라이언트 측에서 다시 가져옵니다.

$fetch가 서버에서 클라이언트로 상태를 전송하지 않기 때문입니다.

따라서 클라이언트가 데이터를 다시 가져와야 하므로 가져오기는 양쪽에서 실행됩니다.

 

클라이언트 측에서만 실행되는 모든 메소드에 사용할 수 있다.

// ./page/contact.vue
<script setup lang="ts">
function contactForm() {
  $fetch('/api/contact', {
    method: 'POST',
    body: { hello: 'world '}
  })
}
</script>

<template>
  <button @click="contactForm">Contact</button>
</template>

$fetchNuxt 2용으로 만들어진 @nuxt/http  @nuxtjs/axios 대신 Nuxt에서 HTTP 호출을 수행하는 데 선호되는 방법입니다 .

useAsyncData

(https://nuxt.com/docs/api/composables/use-async-data)

useAsyncData는 SSR 친화적인 컴포저블에서 비동기적으로 확인되는 데이터에 대한 액세스를 제공합니다.

페이지, 컴포넌트 및 플러그인 내에서 useAsyncData를 사용하여 비동기적으로 확인되는 데이터에 액세스할 수 있습니다.

<script setup>
const { data, pending, error, refresh } = await useAsyncData(
  'mountains',
  () => $fetch('https://api.nuxtjs.dev/mountains')
)
</script>

Watch 매개변수

내장 watch옵션을 사용하면 변경 사항이 감지되면 페처 기능을 자동으로 다시 실행할 수 있습니다.

<script setup>
const page = ref(1)
const { data: posts } = await useAsyncData(
  'posts',
  () => $fetch('https://fakeApi.com/posts', {
    params: {
      page: page.value
    }
  }), {
    watch: [page]
  }
)
</script>

 

매개변수

key: 데이터 가져오기가 요청 전반에 걸쳐 적절하게 중복 제거될 수 있도록 보장하는 고유 키입니다. 키를 제공하지 않으면 의 인스턴스 행 번호와 파일 이름에 고유한 키가 useAsyncData생성됩니다.

handler: 실제 값을 반환해야 하는 비동기 함수(예:  undefined 또는 null 이면 안 됨). 그렇지 않으면 요청이 클라이언트 측에서 중복될 수 있습니다.

options:

  • server: 서버에서 데이터를 가져올지 여부 (기본값은 true)
  • lazy: 클라이언트 측 탐색을 차단하는 대신 경로를 로드한 후 비동기 기능을 해결할지 여부(기본값은 false)
  • immediate: false로 설정하면 요청이 즉시 실행되지 않습니다. (기본값은 true)
  • defaultdata: 비동기 함수가 해결되기 전에 의 기본값을 설정하는 팩토리 함수 - lazy: true or immediate: false옵션 과 함께 유용함
  • transform handler: 해결 후 함수 결과를 변경하는 데 사용할 수 있는 함수
  • pick handler: 함수 결과 에서 이 배열의 지정된 키만 선택합니다.
  • watch: 자동 새로고침을 위해 반응 소스를 관찰합니다.
  • deep: 깊은 참조 객체의 데이터를 반환합니다(기본값 true). false얕은 참조 객체에 데이터를 반환하도록 설정할 수 있으며 , 이는 데이터가 깊게 반응할 필요가 없는 경우 성능을 향상시킬 수 있습니다.

 

useFetch

(https://nuxt.com/docs/api/composables/use-fetch)

SSR 친화적인 컴포저블을 사용하여 API 엔드포인트에서 데이터를 가져옵니다. 

이 컴포저블은 AsyncData와 $fetch를 사용하는 데 편리한 wrapper를 제공합니다.

URL 및 fetch 옵션을 기반으로 키를 자동으로 생성하고 서버 경로를 기반으로 요청 URL에 대한 유형 힌트를 제공하며 API 응답 유형을 추론합니다. 

useFetch는 설정 기능, 플러그인 또는 경로 미들웨어에서 직접 호출할 수 있는 합성 파일입니다. 이것은 반응형 컴포저블을 반환하고  Nuxt 페이로드에 응답을 추가하는 작업을 처리하여 페이지가 렌더링간의 조화를 공급할 때 클라이언트 측의 데이터를 다시 가져오지 않고 서버에서 클라이언트로 전달할 수 있습니다.

<script setup>
const route = useRoute()

const { data, pending, error, refresh } = await useFetch(`https://api.nuxtjs.dev/mountains/${route.params.slug}`, {
  pick: ['title']
})
</script>

 

query를 사용해 fetching을 확장시킬수 있습니다.

const param1 = ref('value1')
const { data, pending, error, refresh } = await useFetch('https://api.nuxtjs.dev/mountains', {
  query: { param1, param2: 'value2' }
})

// https://api.nuxtjs.dev/mountains?param1=value1&param2=value2

Interceptors 사용이 가능하다

const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
  onRequest({ request, options }) {
    // Set the request headers
    options.headers = options.headers || {}
    options.headers.authorization = '...'
  },
  onRequestError({ request, options, error }) {
    // Handle the request errors
  },
  onResponse({ request, response, options }) {
    // Process the response data
    localStorage.setItem('token', response._data.token)
  },
  onResponseError({ request, response, options }) {
    // Handle the response errors
  }
})

커스텀fetch 관련(https://nuxt.com/docs/examples/advanced/use-custom-fetch-composable)

매개변수

URL: 가져올 URL입니다.

Options( unjs/ofetch 옵션 및 AsyncDataOptions 확장 ):

모든 fetch option에는 computed와 ref의 값을 줄 수 있다. 이를 감시하고 업데이트된 경우 새 값으로 자동으로 새 요청이 수행된다.

 

options: useAsyncData의 옵션을 사용할수 있다. 

  • server: 서버에서 데이터를 가져올지 여부 (기본값은 true)
  • lazy: 클라이언트 측 탐색을 차단하는 대신 경로를 로드한 후 비동기 기능을 해결할지 여부(기본값은 false)
  • immediate: false로 설정하면 요청이 즉시 실행되지 않습니다. (기본값은 true)
  • defaultdata: 비동기 함수가 해결되기 전에 의 기본값을 설정하는 팩토리 함수 - lazy: true or immediate: false옵션 과 함께 유용함
  • transform handler: 해결 후 함수 결과를 변경하는 데 사용할 수 있는 함수
  • pick handler: 함수 결과 에서 이 배열의 지정된 키만 선택합니다.
  • watch: 자동 새로고침을 위해 반응 소스를 관찰합니다.
  • deep: 깊은 참조 객체의 데이터를 반환합니다(기본값 true). false얕은 참조 객체에 데이터를 반환하도록 설정할 수 있으며 , 이는 데이터가 깊게 반응할 필요가 없는 경우 성능을 향상시킬 수 있습니다.
//동일 문

const {data: items} = await useFetch('/api/params')


const {pending, data: items} = useFetch('/api/params',{
  lazy: false
  server: false // 클라이언트 사이드 렌더링 이 됨
})

const {pending, data: items} = await useLazyFetch('/api/params')

Transform Handler

const {data: items} = useFetch('/api/params',
 {
  lazy:false,
  transform:(items)=>{
    return items.map((item) => ({
      id: item.id
      title: item.title
      image: item.image
     }));
    }
  }
)

Pick Handler

const {data: items} = useFetch('/api/params',
 {
  lazy:false,
  pick:['id','title','image]
)

 

useAsyncData & useFetch 의 반환값

반환 값

  • data: 전달된 비동기 함수의 결과입니다.
  • pending: 데이터를 아직 가져오는 중인지 여부를 나타내는 부울입니다.
  • refresh/ execute: 함수에서 반환된 데이터를 새로 고치는 데 사용할 수 있는 함수입니다 handler.
  • error: 데이터 가져오기에 실패한 경우 오류 개체입니다.
  • status: 데이터 요청 상태를 나타내는 문자열( "idle", "pending", "success", "error").
  • 기본적으로 Nuxt는 refresh가 다시 실행되기 전에 완료될 때까지 기다립니다.
<script setup>
  const param1 = ref('value1')
  const { data:items, pending, error, refresh } = await useFetch('https://api.nuxtjs.dev/mountains', {
    query: { param1, param2: 'value2' }
  })
</script>

<template>
  <div v-if="pending">
    <p>로딩중</p>
  </div>
  
  <div v-else-if="error">
    <p>Error Code {{ error?.message }}</p>
  </div>
  
  <div v-else>
   <button @click="refresh"> 새로고침 </button>
   	<div>
     <div v-for="item in items" :key="item.id">
      {{item}}
     </div>
    </div>
  </div>
</template>

다중 Fetching

<script setup lang="ts">
const {
  pending,
  data: productInfo,
  refresh,
} = useAsyncData(
  "productInfo",
  async () => {
    const [products, catagories]: any = await Promise.all([
      $fetch("/api/products"),
      $fetch("/api/products/categories"),
    ]);
    return {
      products,
      catagories,
    };
  },
  {
    lazy: false,
    transform: (productInfo) => {
      return {
        catagories: productInfo.catagories,
        products: productInfo.products.map((product: any) => ({
          id: product.id,
          title: product.title,
          image: product.image,
        })),
      };
    },
  }
);
</script>
728x90

'VueJS > NUXT' 카테고리의 다른 글

Nuxt Kakao Map 연동  (0) 2024.08.25
NuxtJS__app, layout, pages, components, errorpage  (1) 2023.11.14
반응형

"this" keyword in Arrow Functions

this 키워드를 사용해야하는 경우를  arrow function 제외하고 사용할 수 있다.

const button = document.querySelector("button");

button.addEventListener("click", function () {
  console.log(this);
  console.log("clicked");
});

 

arrow function을 사용해서 this를 불러내면 window object들을 불러낼것이다.

const hi = {
  hi: "hoho",
  bye: false,
  age: 2,
  addMeet() {
    this.age++;
  },
};
console.log(hi);
hi.addMeet();
console.log(hi);

hi.addMeet();
hi.addMeet();
hi.addMeet();
hi.addMeet();
hi.addMeet();

console.log(hi);

 

Arrow Function에서의 return

const email = ["korea@korea.com", "naver@naver.com", "hoho@hoho.com"];
const haha = email.map((e, index) => {
  return {username: e.split("@")[0], index};
});
console.log(haha);


const email = ["korea@korea.com", "naver@naver.com", "hoho@hoho.com"];
const haha = email.map((e, index) => ({username: e.split("@")[0], index}));
console.log(haha);

 

Funtion에서의 기본값

function sayHello(name = "choi") {
  return "Hello " + name;
}
console.log(sayHello());
console.log(sayHello("jaja"));

const helloMechine = (name = "hihi") => {
  return "Hello " + name;
};
console.log(helloMechine());
console.log(helloMechine("chimp"));

 

Template Literals

const wrapper = document.querySelector(".wrapper");

// const addWelcome = () => {
//   const div = document.createElement("div");
//   const h1 = document.createElement("h1");
//   h1.innerText = "Hello";
//   h1.className = "hiTitle";
//   div.append(h1);
//   wrapper.appendChild(div);
// };



const addWelcome = () => {
  const div = `
    <div>
        <h1 class="hiTitle">Hello</h1>
    </div>
  `;
  wrapper.innerHTML = div;
};

addWelcome();

둘다 같은 결과를 내며 꼭 백틱(  )을 사용해야한다.

const wrapper = document.querySelector(".wrapper");

const laugh = ["haha", "hoho", "hehe", "jaja", "hihi"];

const list = ` 
<h1>Laugh List</h1>
<ul>
${laugh.map((item) => `<li>${item}</li>`).join("")}
</ul>
`;

wrapper.innerHTML = list;

 

const wrapper = document.querySelector(".wrapper");
const cssIncome = (css) => {
  console.log(css);
};

cssIncome(`border-radius:10px;color:blue;`);
cssIncome`border-radius:10px;color:blue;`;

// const styled = (newEle) => {
//     const el = document.createElement(newEle);
//     return el;
// };
// const title = styled("h1")`border-radius:10px;color:blue;`; 이렇게 하면 에러가 난다. 에러가 나는 이유는 funtion을 두번 호출했다는 의미이다.

const styled = (newEle) => {
  const el = document.createElement(newEle);
  return (args) => {
    console.log(args[0]);
    const styled = args[0];
    el.style = styled;
    return el;
  };
};
const title = styled("h1")`
  border-radius: 10px;
  color: blue;
`;
const span = styled("span")`
  font-size: 25px;
  color: red;
`;

title.innerText = "wow";
span.innerText = "suprised!!";

wrapper.append(title, span);

 

repeat / includes / startWithin / endWithin / forEach / map / replace ...

728x90

'CODING PRACTICE > JavaScript & ES6' 카테고리의 다른 글

ES6__003(Spread / Rest / for of Loop)  (0) 2023.12.20
ES6__002(Array & Destructuring)  (0) 2023.12.01
Pagination 공식  (0) 2023.08.10
2023/04/11__JavaScript__05  (0) 2023.04.13
2023/04/10__JavaScript__04  (0) 2023.04.12
반응형

들어가기전..

자바스크립트는 개발자를 그닥 도와주지 않는다. 실수를 피할 수 있도록 만들어지지 않았다.

예를 들어 아래와 같은 문장을 작성을 해도 동작을 한다.

const a = 1+true;

 

이러한 동작들은 실행이 되고 나서야 에러 메세지를 볼 수 있게 되는데 이를 "런타임 에러"  라고 한다. 

실질적 이런 런타임 에러를 마주치게 될 사람들 중 하나가 실제 사용자라는 것이다. 

 

이를 위해 코드를 실행하기 전에 이런 에러를 잡아내기 위해 우리는 TypeScript를 사용해야한다.

TypeScript

타입스크립트는 strongly typed(강타입) 프로그래밍 언어이다. 

C#, Java, Go, Rust같은 언어에서 넘어왔다면 프로그래밍 언어라 할 때 컴파일러를 떠올리게 되는데 이는 코드를 컴파일 해서 0101로 바꿔주거나 어셈블리코드나 바이트 코드 혹은 기계가 실행할 수 있는 다른 종류의 코드로 변환이 된다. 

타입스크립트의 경우에는 작성한 코드가 자바스크립트로 변환이 된다. 또한 변환된 자바스크립트 안에서 실수가 일어나지 않게 확인을 해준다.

const me = {
    i:"hellicat"
}
me.hihi()

위와 같이 작성하고 JS 경우 vsCode에서는 에러 표시가 나지 않지만 타입스크립트 세계에서는 hihi에 빨간 밑줄이 쳐져 있을 것이다.

const b = [1,2,3] + false

이 경우에도 JS에서는 1,2,3false라고 도출되지만 TS에서는 절대로 값을 내어주지 않는다.

 

function devide(a,b){
    return a / b
}

devide('hoho','haha')

뿐만 아니라 함수에서도 위의 함수는 JS에는 결과값이 NaN로 나오며 실행이 되지만 TS에서는 절대로 작동을 안한다.

 

Type System

JS에서는 그냥 변수만 만들어주면 동작하는데 큰 무리가 없었지만 TS에서는 이를 type을 지정해주어야 한다. 

let a ='hoho'
a = 'haha'
a = 1 // <=Error

위와 같이 작성시 TS는 자동으로 a를 String 타입만 받게 인식을한다. 평소 JS에서는 동작했을 문구였지만 TS에서는 이를 거절한다.  

또한 마지막 a = 1 에 마우스를 가져 올리면 let a: string이라는 type 선언이 되 있음을 볼 수 있을 것이다.

 

let a = [1,2,3]  // Type of number[]
let b = ["1","2","3"]  //  Type of string[]
let c = false  // Type of boolean
let d = "hoho"  // Type of string
let e = 13  // Type of number
let f    // Type of any
let g : number[]=[]
g.push('12') // Error
g.push(13) // None Error

// 항상 아래와 같이 할 필요는 없다 typescript가 추론하게 하면 됨
let h : string[] = ['1','2','3']
let i : boolean = true
let j : string = '11122hahah'
let k : number = 13
let l : any;

Alias Type

const family : {
    father:string,
    mother:number, 
    brother:boolean
    sister?:string[] // sister에는 ?가 있기 때문에 required가 아니다.
    } = {
	father: '60',
    mother: 100,
    brother: false,
}

if(family.mother < 120){

} else if(family.sister && family.sister.length < 10){
 //family.sister의 값이 required가 아니기 때문에 undefined checking도 해야한다.
}


////////////

/* FamilyDesc라는 Alias type을 만들어 다음과 같이 사용할 수도 있다. */

type SuperSisters = string[]

type FamilyDesc = {
    father:string,
    mother:number, 
    brother:boolean
    sister?:SuperSisters
}

const myFamily : FamilyDesc = {
	father: '60',
    mother: 100,
    brother: false,
}

const yourFamily :FamilyDesc={
    father : '80',
    mother: 200,
    brother : true,
    sister:['King - Kong' , 'Gozilla']
}

 

type SuperSisters = string[]

type FamilyDesc = {
    father:string,
    mother:number, 
    brother:boolean
    sister?:SuperSisters
}

const animalFam:FamilyDesc[] = []

function familyMaker (father:string, mother:number, brother:boolean, sister?:SuperSisters){
    return animalFam.push({father,mother,brother,sister})
}

familyMaker('hoho',3,true)

console.log(animalFam)


////////////////////

type SuperSisters = string[]

type FamilyDesc = {
    father:string,
    mother:number, 
    brother:boolean
    sister?:SuperSisters
}

const animalFam:FamilyDesc[] = []

function familyMaker (father:string, mother:number, brother:boolean, sister?:SuperSisters) :FamilyDesc {
    const family = {father,mother,brother,sister}
     animalFam.push(family)
     return family
}

familyMaker('hoho', 3 ,true)

console.log(animalFam)

 

Type Read Only

원하는 타입에 readonly 속성을 추가 할 수 있다.

요소들을 ' 읽기전용 ' 으로 만들어준다.

type SuperSisters = string[]

type FamilyDesc = {
    father:string,
    mother:number, 
    readonly brother:boolean
    sister?:SuperSisters
}

const animalFam:FamilyDesc[] = []

function familyMaker (father:string, mother:number, brother:boolean, sister?:SuperSisters) :FamilyDesc {
    const family = {father,mother,brother,sister}
     animalFam.push(family)
     return family
}

familyMaker('hoho', 3 ,true)

animalFam[0].sister = ['hoho'] 
animalFam[0].sister = [...animalFam[0].sister ,'haga'] // read only가 아니기에 값을 변경하고 넣을 수 있다.
animalFam[0].brother = false // read only이기에 값을 변경 할 수 없다.

 

TUPLE

tuple은 특정 위치에 특정 타입이 있어야한다.

const a :[string,number, boolean] = ['a', 2, false]

console.log(a[0])
a[0]='hoho'

console.log(a[0])
a[0]=1 // => Error

 

Null & Undefined & Any

const a : undefined = undefined

type Hoho ={
    haha? : string  // => string | undefined
}

const b : null = null

any 같은 경우 TS로부터 빠져나올 경우에 쓰는 타입이다. 

TS 설정에 any 사용을 막기 위해 추가할 수 있는 몇가지 규칙이 있다.

첫째로는 any를 사용하지 않는 것이다.

unknown

두번째로 어떤 타입인지 모르는 변수는 TS에서 'unknown'이라는 타입을 쓸 수가 있다. unknown 타입을 사용하게 되면 TS로부터 일종의 보호를 받게 된다. 어떤 작업을 하려면 이 변수의 타입을 먼저 확인해야하는 방식으로 말이다. 아래의 예시가 있다.

let a :unknown
a = 3
let b = a + 1
console.log(b)

// a의 타입을 명확하게 지정해주지 않았기 때문에 Error

let a :unknown ; 

if ( typeof a === "number"){
    let b = a + 1
    console.log(b)
}
if( typeof a === "string"){
    let b = a.toUpperCase()
    console.log(b)
}

 

Void

다음으로 void가 있는데 아무것도 return하지 않는 함수를 대상으로 사용한다. function에 마우스를 올리면 void를 볼 수 있다.

또한 void는 따로 지정해 줄 필요는 없다.

function laugh (){
    console.log('hohoh')
}

Never

never는 함수가 절대 return하지 않을 때 발생한다. 이렇게 하면 return에 오류가 생긴다. never는 사용 빈도가 매우 낮을 것이다.

function laugh ():never{
  return  console.log('hohoh')
}

// 하지만 return을 사용하지 않고 오류는 발생시킬수는 있다.
function laugh ():never{
 throw new Error('xxx')
}



function laugh (power:string|number){
    if(typeof power === 'string'){
        //작동 코드
    } else if (typeof power === 'number'){
        //작동 코드
    } else {
        //never가 리턴되기 때문에 작동코드는 동작하지 않는다.
        throw new Error("Wrong!!!")
    }
}

laugh(false)
728x90
반응형

React-Native CLI를 설치하기 위한 자료는 (https://reactnative.dev/docs/environment-setup?guide=native&platform=android&os=macos) 에 나와있다.

 

React Native CLI로 작업하기 위해서는 <개발하는 OS>와 <타겟되는 OS>를 정해야한다. 필자의 같은 경우에는 <개발 OS>는 <Mac OS> // <타겟 OS>는 Android 로 정하고 개발환경을 만들어주었다. 

 

1. Node & Watchman 설치

brew install node
brew install watchman

 

2. Java Develope Kit (JDK)

brew tap homebrew/cask-versions
brew install --cask zulu11

# Get path to where cask was installed to double-click installer
brew info --cask zulu11

 

환경변수 설정 (zshrc)

터미널에
vim ~/.zshrc 입력

e 눌러 edit mode
page down 눌러 제일 아래로 이동후 
insert 활성하고 아래 작성

export JAVA_HOME="/Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home"
export PATH=${PATH}:$JAVA_HOME/bin

작성후 ctrl+c 누르고 :wq 눌러 vim~/.zshrc에서 나오기

터미널 종료 후 다시 시작하여

echo $JAVA_HOME

입력후 경로 확인

 

3. Android 개발 환경 설정

  • android sdk
  • android sdk platform
  • android virtual device

설치

  • 3-1. Android SDK 설치 

2023년 11월 16일 기준 Android 13(Tiramisu) 설치를 권고함

Android Studio 열고 More Action에서 SDK MANAGER로 들어가서 설치

  • Android SDK Platform 33
  • Intel x86 Atom_64 System Image또는 Google APIs Intel x86 Atom System Image또는 (Apple M1 Silicon의 경우)Google APIs ARM 64 v8a System Image

 

  • 3-2 Android_HOME 환경 변수 구성
터미널에
vim ~/.zshrc 입력

e 눌러 edit mode
page down 눌러 제일 아래로 이동후 
insert 활성하고 아래 작성

export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/platform-tools

작성후 ctrl+c 누르고 :wq 눌러 vim~/.zshrc에서 나오기

 

4. Project 설치

원하는 폴더 만들고 vscode 열어 아래 명령어 입력

npx react-native@latest init AwesomeProject

 

5. 프로젝트 시작

npm run android
혹은 
npx react-native run-android

 

 

설치 중 오류처리

Project 설치시 Ruby의 버전이 낮아 Cocoapods를 설치를 못하는 경우가 있었다.

Ruby의 버전을 업데이트하기 위한 사용한 방법을 나열하겠다.

brew update
brew upgrade

brew install rbenv ruby-build
// 위 설치 후 rbenv로 설치할 수 있는 Ruby버전을 확인한다.

rbenv install -l 
// ruby 홈페이지가서 스텐다드 버전을 확인하고 다운 받았다.

rbenv install 3.x.x
// 설치 후 설치된 버전으로 바꾸기 위해

rbenv global 3.x.x 
//입력

ruby --version
//위를 입력하여 본인이 설치한 버전이 바뀌었는지 확인한다. 
// 터미널을 껏다 켜고 다시 한번 더 확인한다.

버전이 바뀌지 않았을 때

rbenv init

// 를 입력하면

# Load rbenv automatically by appending
# the following to ~/.zshrc:

eval "$(rbenv init - zsh)"

//라고 나오는데 이것을

vim ~/.zshrc

// 위를 입력하여 편집 모드로 들어가 제일 하단에 

eval "$(rbenv init - zsh)"

// 위의 문구를 추가해주자
// 추가 후 터미널을 껏다 다시 켜고
// ruby --version을 눌러준다.

 

Watchman Error

npm run android를 하고 watchman 터미널이 따로 등장하는데 등장과 함께 에러코드가 나왔다.

...내용중략~~~ watchman error operation not permitted ~~~~내용중략...

 

문구가 나왔을 시 순차적으로 입력하자.

watchman watch-del-all
watchman shutdown-server

 

또한 .watchmanconfig 파일 내부는 

{}

중괄호가 입력되어 있어져야한다.

728x90
반응형

TouchableOpacity / TouchableHighlight / TouchableWithoutFeedback / Pressable

Props로 onPress로 이벤트를 부여할 수 있다.

TOpacity 경우 천천히 투명도가 0에서 1로 가는 느낌이고

THighlight 경우 반짝 하는 느낌

TWithoutFeedback은 아무 효과가 없다

또한 문서를 참조하여 props를 추가해 다른 효과들을 바꿔줄 수 있다.

Pressalbe은 TWithoutFeedback 과 비슷하지만 더 새로운 것이다. 이를 가지고 onLongPress같은 것들을 사용할 수 있다.

 

import {
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
  TouchableHighlight,
  TouchableWithoutFeedback,
  Pressable,
} from "react-native";
export default function App(){
	const [active, setActive] = useState(true);
	const travel = () => setActive(false);
	const work = () => setActive(true);
	return (
		<View style={styles.container}>
			<StatusBar style="auto" />
			<View style={styles.header}>
				<TouchableOpacity onPress={work}>
					<Text>
						Work
					</Text>
				</TouchableOpacity>
				<TouchableOpacity onPress={travel}>
					<Text>
						Travel
					</Text>
				</TouchableOpacity>
			</View>	
		</View>	
	);
}

 

TextInput

import {StatusBar} from "expo-status-bar";
import {
  StyleSheet,
  Text,
  View,
  TextInput,
} from "react-native";
import {theme} from "./color";
import {useEffect, useState} from "react";

export default function App() {
  const [text, setText] = useState("");
  const travel = () => setActive(false);
  const work = () => setActive(true);
  const textChange = (payload) => setText(payload);
  return (
    <View style={styles.container}>
      <StatusBar style="auto" />
      <View>
        <TextInput
          style={styles.input}
          placeholder={active ? "ADD TODO" : "WHERE DO YOU WANNA GO?"}
          keyboardType="default"
          returnKeyType="done"
          onChangeText={textChange}
          onSubmitEditing={addTodo}
          value={text}
        />
      </View>     
    </View>
  );
}

//style
input: {
    backgroundColor: "white",
    paddingVertical: 10,
    paddingHorizontal: 20,
    borderRadius: 20,
    marginTop: 20,
    fontSize: 18,
    marginVertical: 20,
  },

 

처음에 TextInput사용하게 되면 화면에 나오지 않아 당황할수도 있다. 그럴땐 스타일링을 줘 배경색을 다르게 해보자.

keyboardType으로 모바일의 키보드 종류에 맞는 배열로 바꿔줄수 있다.

props는 android에서 호환되는것과 ios에서 호환되는것들이  혹은 둘다 호환되는 것들이 있으니 잘 참고해서 사용하자

onSubmitEditing으로 submit을 할수 있게 만들수 있다.

 

AsyncStorage

npx expo install @react-native-async-storage/async-storage

 

이를 사용하기 위해서는 try catch 문을 사용할것을 강력권고한다.

Alert API

Alert API는 대화창을 실행시킨다. 두가지 옵션이 있는데 alert()과 prompt()가 잇는데 prompt는 ios에서만 작동 가능하다.

Alert.alert(제목, 내용?, 버튼?, 옵션?)
//? 표시는 type자로 required가 아님을 나타냄
// 버튼은 array of object로 text / onPress / style 을 넣을 수 있으며 style은 ios 한정이다.
  const deleteTodo = async (key) => {
    Alert.alert("R U SURE?", "REALLY DELETE?", [
      {text: "cancel"},
      {
        text: "ok",
        style: "destructive",
        onPress: async () => {
          const newToDos = {...toDos};
          delete newToDos[key];
          setToDos(newToDos);
          await saveToDos(newToDos);
        },
      },
    ]);
    return;
  };

 

728x90

'React-Native > Basic with Expo' 카테고리의 다른 글

React-Native__Basic-001 with EXPO  (0) 2023.11.16
React-Native__Basic-002 with EXPO  (0) 2023.11.15
반응형

React Native의 Layout System

React Native에서는 display:block , inline-block,grid 이러한 것들이 없다.

오로지 flexbox뿐이다. 

웹에서는 display:flex ; flex-direction:column을 주어 세로 정렬이 가능했지만 모바일에서는 flex-direction:column이 default 값이다. 또한 <View>사용해 넘차나는 부분을 스크롤할수가 없다.

 

모바일에서는 너비와 높이를 기반한 어떠한것들을 만드는 것은 좋은 생각이 아니다.

flex를 사용한 비율을가지고 만드는 것을 추천한다.

export default function App(){
	return (
    	<View style={{flex:1}}>
        	<View style={{flex:1, backgroundColor:"orange"}}></View>
            <View style={{flex:5, backgroundColor:"blue"}}></View>
            <View style={{flex:2, backgroundColor:"red"}}></View>
        </View>
}

 

또한 작업진행시 바탕색을 넣으며 비율을 보며 작업을 하는게 용의하다

<ScrollView>

export default function App(){
	return (
    	<View style={{flex:1}}>
          <View style={{flex:1, backgroundColor:"orange"}}>
            	<Text>hoho</Text>
                <Text>haha</Text>
                <Text>hehe</Text>
            </View>
            <View style={{flex:5, backgroundColor:"blue"}}>
            	<Text>huhu</Text>
                <Text>hyhy</Text>
                <Text>papa</Text>
            </View>
            <View style={{flex:2, backgroundColor:"red"}}>
            	<Text>pepe</Text>
                <Text>popo</Text>
                <Text>pipi</Text>
            </View>
        </View>
}

이렇게 만들시 웹과 다르게 스크롤이나 드래그를 할 수  없다.

 

이걸 스크롤하게 만들기 위해서는 

export default function App(){
	return (
    	<ScrollView style={{flex:1}}>
          <View style={{flex:1, backgroundColor:"orange"}}>
            	<Text>hoho</Text>
                <Text>haha</Text>
                <Text>hehe</Text>
            </View>
            <View style={{flex:5, backgroundColor:"blue"}}>
            	<Text>huhu</Text>
                <Text>hyhy</Text>
                <Text>papa</Text>
            </View>
            <View style={{flex:2, backgroundColor:"red"}}>
            	<Text>pepe</Text>
                <Text>popo</Text>
                <Text>pipi</Text>
            </View>
        </ScrollView>
}

이렇게 하면 세로로 드래그할수 있는 화면이 나오게 된다.

 

이것을 가로로 드래그 할 수 있는 화면으로 구성하려면

<ScrollView 
  horizontal
  pagingEnabled
  showHorizontalScrollIndicator
  contentContainerStyle={{flex:1}}
  ></ScrollView>

horizontal을 추가하면된다. 또한 style은 그냥 style을 사용하면 안되고 contentContainerStyle을 사용해야한다.

pagingEnabled을 추가하게 되면 자유분방한 스크롤이 하나씩 넘어가게 끔 조정된다.

showHorizontalScrollIndicator는 overflow-x:hidden 정도로 생각하면된다.

그밖에 props가 많으니 document을 보고 참고하자.

 

디바이스 화면의 크기를 알아내는 방법은 Dimensions를 사용하면 된다.

const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;

const {width:SCREEN_WIDTH,height:SCREEN_HEIGHT} = Dimensions.get('window')

 

User Location

유저의 위치를 알기 위한 방법이다.

npx expo install expo-location

 

먼저 권한 요청을 해야한다. 권한 요청은 Location.requestForegroundPermissionAsync()를 사용하며 꼭 import * as Location from 'expo-location'을 작성하여 불러오자

import {StatusBar} from "expo-status-bar";
import * as Location from "expo-location";
import {useEffect, useState} from "react";

const year = new Date().getFullYear();
const month =
  new Date().getMonth() > 10
    ? "0" + (new Date().getMonth() + 1)
    : new Date().getMonth() + 1;
const date =
  new Date().getDate() < 10 ? "0" + new Date().getDate() : new Date().getDate();

const base_url = (lat, lon) =>
  `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${API_KEY}&units=metric`;

const {width: SCREEN_WIDTH} = Dimensions.get("window");

export default function App() {
  const [city, setCity] = useState("Loading.. ");
  const [day, setDay] = useState([]);
  const [ok, setOk] = useState(true);
  
  //권한 요청
  const getLocateWeather = async () => {
    const {granted} = await Location.requestForegroundPermissionsAsync();
    if (!granted) {
      setOk(false);
    }
   
}
  useEffect(() => {
    getLocateWeather();
  }, []);

 

다음은 현재 위치를 알아내기 위해 Location.getCurrentPositionAsync()를 사용한다.  여기서는 몇가지 옵션을 받는데 그중하나가 accuracy인데 얼마나 정확하게 나오게 할지에 대한 부분이다. 여기서 디버깅하면 위도와 경도를 알아낼 수 있다.

export default function App() {
  const [city, setCity] = useState("Loading.. ");
  const [day, setDay] = useState([]);
  const [ok, setOk] = useState(true);

  const getLocateWeather = async () => {
    const {granted} = await Location.requestForegroundPermissionsAsync();
    if (!granted) {
      setOk(false);
    }
    
    const {
      coords: {latitude, longitude},
    } = await Location.getCurrentPositionAsync({accuracy: 5});
   } 
   
  useEffect(() => {
    getLocateWeather();
  }, []);

 

이것을 가지고 본인이 살고 있는 지역을 알아낼 수 있다. 위치를 알아내는 방법은 크게 두가지 인데 geocodeAsync(address) ((주소를 받아서 위도와 경로로 변환)와 reverseGeocodeAsync(location,option) ((위도와 경도를 이용하여 주소를 반환)) 이다.

Location.reverseGeocodeAsync(location,option)를 사용하면 된다. 여기서는 위치와 옵션을 설정한다.

export default function App() {
  const [city, setCity] = useState("Loading.. ");
  const [day, setDay] = useState([]);
  const [ok, setOk] = useState(true);

  const getLocateWeather = async () => {
    const {granted} = await Location.requestForegroundPermissionsAsync();
    if (!granted) {
      setOk(false);
    }
    const {
      coords: {latitude, longitude},
    } = await Location.getCurrentPositionAsync({accuracy: 5});

    const location = await Location.reverseGeocodeAsync(
      {latitude, longitude},
      {useGoogleMaps: false}
    );
}
  useEffect(() => {
    getLocateWeather();
  }, []);

이것을 디버깅하게 되면 ios 시뮬레이터는 항상 미국에 위치해있다고 디버깅 할 것이다. 디버깅을 추적하여 데이터값들을 입력해주면 된다.

 

ICONS 

두번째 주소로 들어가 원하는 아이콘을 찾아서 누른후 나오는 코드들만 추가하면 끝이다. 같은 종류의 코드를 하는것이 tree shaking에 좋을 듯하다.

 

728x90

'React-Native > Basic with Expo' 카테고리의 다른 글

React-Native__Basic-003 with EXPO --TODO  (0) 2023.11.18
React-Native__Basic-001 with EXPO  (0) 2023.11.16
반응형

React Hook Form // https://react-hook-form.com/

 

React Hook Form - performant, flexible and extensible form library

Performant, flexible and extensible forms with easy-to-use validation.

react-hook-form.com

react hook form은 validation이나 에러, 이벤트 같은 필요한 기능들을 넣어서 적은 줄의 코드를 사용해 form을 만들 수 있게 해준다.

아래 코드는 기존 hoor form을 안쓰고 하는경우이다

"use client";
import {SyntheticEvent, use, useState} from "react";

export default function Form() {
  const [username, setUsername] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("")
  const [formErr, setFormErr] = useState("");
  const onUsernameChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
    const {
      currentTarget: {value},
    } = event;
    setUsername(value);
  };
  const onEmailChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
    const {
      currentTarget: {value},
    } = event;
    setEmail(value);
  };
  const onPasswordChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
    const {
      currentTarget: {value},
    } = event;
    setPassword(value);
  };
  const onSubmit = (event: SyntheticEvent<HTMLFormElement>) => {
    event.preventDefault();
    console.log(username, email, password);
    // validation codes..
        // validation codes..
            // validation codes..
                // validation codes..
                    // validation codes..
                        // validation codes..    // validation codes..
                        
  };
  return (
    <div>
      <form action="" onSubmit={onSubmit}>
        <input
          value={username}
          onChange={onUsernameChange}
          type="text"
          placeholder="Username"
          required
        />
        <input
          value={email}
          onChange={onEmailChange}
          type="email"
          placeholder="Email"
          required
        />
        <input
          value={password}
          onChange={onPasswordChange}
          type="password"
          placeholder="password"
          required
        />
        <input type="submit" value="submit"/>
      </form>
    </div>
  );
}

hook form을 사용하지 않게 되면 사용자가 html코드를 건드려 마음대로 설정할수 있다.

또한 좋은 form을 만들려다보면 스크립트 내에서의 validation을 검증하는 코드가 길어진다.

 

React Hook Form Install

npm install react-hook-form

 

How To Use?

1. register

첫번째로 해야할 것은 input을 state와 연결하는 것이다. 다음에 validation을 하고 다음에 err를 다룬다.

console.log(register)를 하게 되면 4가지를 사용할 수 있음을 볼 수 있다. name ,onBlur, onChange ,ref 이것들은 React에서 Input에 활용되는 attribute와 비슷하다.

"use client";

import {useForm} from "react-hook-form";

export default function Form() {
  const {register} = useForm();

  return (
    <div>
      <form>
        <input
          {...register("username")}
          type="text"
          placeholder="Username"
          required
        />
        <input
          {...register("email")}
          type="email"
          placeholder="Email"
          required
        />
        <input
          {...register("password")}
          type="password"
          placeholder="password"
          required
        />
        <input type="submit" value="submit" />
      </form>
    </div>
  );
}

 

2.  watch

"use client";

import {useForm} from "react-hook-form";

export default function Form() {
  const {register, watch} = useForm();
	console.log(watch())
	console.log(watch("email"))
    //원하는 것만 볼 수도 있다.
  return (
    <div>
      <form>
        <input
          {...register("username")}
          type="text"
          placeholder="Username"
          required
        />
        <input
          {...register("email")}
          type="email"
          placeholder="Email"
          required
        />
        <input
          {...register("password")}
          type="password"
          placeholder="password"
          required
        />
        <input type="submit" value="submit" />
      </form>
    </div>
  );
}

콘솔창을 열고 input에 값을 입력하면 register된 input의 값이 바로 변경하는것을 볼 수 있다.

3. validation

register의 두번째 인자로 validation Rule를 적을 수 있게 해준다.

"use client";

import {useForm} from "react-hook-form";

export default function Form() {
  const {register, watch} = useForm();
	console.log(watch())
  return (
    <div>
      <form>
        <input
          {...register("username",{
          required:true,
          })}
          type="text"
          placeholder="Username"
        />
        <input
          {...register("email",{
          required:"Email is Required",
          })}
          type="email"
          placeholder="Email"
        />
        <input
          {...register("password",{
          required:true,
          })}
          type="password"
          placeholder="password"
        />
        <input type="submit" value="submit" />
      </form>
    </div>
  );
}

4. handleSubmit

handleSubmit은 인자로 2개 또는 1개의 함수를 받을 것이다. 1개는 필수로 설정되어 있다.

첫번째 함수는 form이 유효할 때만 실행되는 함수

두번째 함수는 form이 유효하지 않을 때 실행되는 함수

"use client";
import {FieldErrors, useForm} from "react-hook-form";

interface ILoginForm {
  username: string;
  email: string;
  password: string;
}

export default function Form() {
  const {register, watch, handleSubmit} = useForm<ILoginForm>();
  console.log(watch());
  const onValid = (data: ILoginForm) => {
    console.log("OK");
  };
  const onInValid = (errors: FieldErrors) => {
    console.log(errors);
  };
  return (
    <div>
      <form onSubmit={handleSubmit(onValid, onInValid)}>
        <input
          {...register("username", {
            required: "Username is Required",
            minLength: 5,
          })}
          type="text"
          placeholder="Username"
        />
        <input
          {...register("email", {
            required: "Email is Required",
          })}
          type="email"
          placeholder="Email"
        />
        <input
          {...register("password", {
            required: "Password is Required",
            minLength: {
              message: "More Than 5",
              value: 5,
            },
          })}
          type="password"
          placeholder="password"
        />
        <input type="submit" value="submit" />
      </form>
    </div>
  );
}

 

5.formState & Others..

formState는 많은 걸 제공하는 객체인데 그중하나가 errors 이다.

errors로 validate error를 도출해낼수 있다. 

setValue는 이벤트를 통해 원하는 input에 값을 넣어줄 수 있다.

setError는 특정 필드에 대한 에러가 아니라 form에 대한 전체적인 에러를 얘기한다. 예를 들어 fetch를 통한 에러

reset은 submit을 동작하게 되면 input의 값들을 초기화하는 동작을 한다.

resetField는 특정 input값을 초기화 해준다

"use client";

import {FieldErrors, useForm} from "react-hook-form";

interface ILoginForm {
  username: string;
  email: string;
  password: string;
  errors?: string;
}
export default function Form() {
  const {
    register,
    watch,
    handleSubmit,
    formState: {errors},
    setValue,
    setError,
    reset,
    resetField,
  } = useForm<ILoginForm>({
    mode: "onBlur",
  });

  console.log(watch());

  const onValid = (data: ILoginForm) => {
    console.log("OK");
    setError("errors", {message: "backend is offline"});
    resetField("password");
  };
  const onInValid = (errors: FieldErrors) => {
    console.log(errors);
    reset();
  };

  return (
    <div>
      <form onSubmit={handleSubmit(onValid, onInValid)}>
        <input
          {...register("username", {
            required: "Username is Required",
            minLength: {
              message: "more than 5",
              value: 5,
            },
          })}
          type="text"
          placeholder="Username"
          className={`${Boolean(errors.username) ? "bg-red-400" : ""} 
           `}
        />
        <button
          type="button"
          onClick={() => {
            setValue("username", "hihihi");
          }}
        >
          push
        </button>
        <p>{errors.username?.message}</p>
        <input
          {...register("email", {
            required: "Email is Required",
            validate: {
              notGmail: (value) =>
                !value.includes("@gmail.com") || "Gmail is not allowed",
            },
          })}
          type="email"
          placeholder="Email"
        />
        {errors.email?.message}
        <input
          {...register("password", {
            required: "Password is Required",
            minLength: {
              message: "More Than 5",
              value: 5,
            },
          })}
          type="password"
          placeholder="password"
        />
        {errors.password?.message}
        <input type="submit" value="submit" />
        {errors.errors?.message}
      </form>
    </div>
  );
}

 

728x90

'React > React Hook Form' 카테고리의 다른 글

2023/08/31__React(ReactHookForm)  (2) 2023.08.31

+ Recent posts