반응형

 

 

나의 경우는 백단 서버가 이미 존재하여, URI로 요청하는 방식이므로 axios를 사용했다

axios 사용법에 앞서, 기본적인 react 문법을 훑고 지나가자

 

기본 react 문법

아래는 임시로 작성한 Mypage 이다

import {NextPageWithLayout} from "@/pages/_app";
import Layout from "@/components/layout/Layout";
import {useState} from "react";

interface itemListProps {
    id: string
    title: string
}

const MyPage: NextPageWithLayout = () => {

    //기본적인 변수 선언법
    const [nickname, setNickname] = useState<string>('')
    const [age, setAge] = useState<number>(0)
    const [isModalOpen, setIsModalOpen] = useState(false)
    const [itemList, setItemList] = useState<itemListProps[]>([])

    return(
        <div> mypage </div>
        <div>
        	{itemList.map((item, index) => (
                <div key={item.id}>{item.title}</div>
            ))}
    )
}

MyPage.getLayout = function getLayout(page) {
    return <Layout>{page}</Layout>
}

export default MyPage

 

useState

변수선언은 useState 를 이용하여 선언한다. 우측 괄호 안에 값을 넣는 것으로 초기화 가능하다

또한 List 선언의 경우 type을 정해서 itemListProps 라는 type을 정해서 리스트화 시켜줬다.

 

parameter type

특히 api와 통신하는 값을 받는 리스트의 경우 위와 같이 type을 interface로 선언해주고 사용하자.

n명의 개발자가 동시에 개발할때는 type이 정해져있지않다면 중구난방 난리가 날것이기 때문...

 

자세한 상황은 아래 api 연결할때 설명하겠다

Map

리스트의 경우 map 을 이용하여 풀어낼 수 있다.

첫번째 인자로 객체가 들어가고, 두번째는 index가 오게되는데 map 안쪽에 첫번째 요소에 key 속성이 꼭 있어야한다

만약 아래와 같이 코드를 만들경우 에러가 날것이다

return(
    <div> mypage </div>
    <div>
        {itemList.map((item, index) => (
            <div>{item.title}</div>
        ))}
)

 

key를 꼭 붙여주도록 하자

 

 

install axios

프로젝트의 루트위치에서 터미널을 실행하고 아래와 같은 커맨드를 입력한다

$ npm install axios

 

pakage.json, package-lock.json 파일이 자동으로 수정될것이다

 

axios 설정

루트폴더에 api 폴더를 생성하고 client.tsx 파일을 생성하자

 

이곳에 axios 의 모든 설정을 세팅할것이다

get, post, multipartfile을 같이 보낼경우 3가지의 경우를 연결하겠다

 

▼client.tsx

import axios from 'axios';


class Client {

    async get(url: string, params?: any) {
        try {
            const headers = {
                'Content-Type': 'application/json',
                'X-Requested-With': 'XMLHttpRequest',
            }

            const response = await axios.get(url, {
                params: params,
                headers: headers,
            });
            return response.data;
        } catch (error) {
            console.error(error);
            throw error;
        }
    }

    async post(url: string, params?: any) {
        try {
            const headers = {
                'Content-Type': 'application/json',
                'X-Requested-With': 'XMLHttpRequest',
            }

            const response = await axios.post(url, params, {
                headers: headers,
            });
            return response.data;
        } catch (error) {
            console.error(error);
            throw error;
        }
    }

    async postMultipartFile(url: string, params: FormData) {
        try {
            const headers = {
                'Content-Type': 'multipart/form-data',
                'X-Requested-With': 'XMLHttpRequest',
            }
            const response = await axios.post(url, params, {
                headers: headers,
            });
            return response.data;
        } catch (error) {
            console.error(error);
            throw error;
        }
    }

}

export const client = new Client();

 

이렇게 소스를 따로 뗀 이유는 api 통신이 필요한 프로젝트 내 모든 페이지에서 별도로 연결을 해주어야하기 때문이다

가독성에 안좋고 스프링시큐리티 등의 이유로 인하여 header에 특정 텍스트/쿠키 등을 태워서 보내야 하는 경우 이곳에서 한번에 처리가 가능하다.

 

axios 적용 테스트

자 이제 실제 페이지에서 사용해봅시다

const MyPage: NextPageWithLayout = () => {

    //기본적인 변수 선언법
    const [nickname, setNickname] = useState<string>('')
    const [age, setAge] = useState<number>(0)
    const [isModalOpen, setIsModalOpen] = useState(false)
    const [itemList, setItemList] = useState<itemListProps[]>([])

    useEffect(() => {
        client.get("/api/어쩌고저쩌고")
            .then((response) => {
                console.log(response)
            }).catch((error) => {
                console.log(error)
            })
    },[])
    
    return(
        <div> mypage </div>
    )
}

 

mypage에 진입하자마자 useEffect 가 실행되고 만들어놓은 axios get 통신을 진행하게된다

그런데 보면... 코드상에서 직접 api path 를 입력하게되는데 딱봐도..너무불안하다

실수로 오타라도 나면 url 호출이 안되면서 에러가 빡빡 생길 조짐이 보인다...

그리고 path 가 일괄변경되면 이렇게 호출한 api 로직부분을 하나하나 찾아서 고쳐줘야한다..

 

그래서 api 통신부분을 따로 빼기로했다

axios 실제 적용

 

 

api 폴더 안에 패키지 하나 + tsx 파일 하나 선언해주자

보통 내가 쓰는 도메인 위주로 하면된다

user 관련 api 면 user.. main 페이지 관련 api 면 main.. menu.. category..등등

import {client} from "@/api/client";

class MyPageApi {
    /**
     * ㅇㅇㅇ페이지 :: ㅁㅁㅁ 조회
     */
    test1 = async () => {
        return await client.get('/api/어쩌고주소')
    }

    /**
     * ㅇㅇㅇ페이지 :: ㅁㅁㅁ 조회
     * @param title
     */
    test2 = async (title:string) => {
        return await client.get('/api/어쩌고주소',title)
    }

    /**
     * ㅇㅇㅇ페이지 :: ㅁㅁㅁ 수정
     */
    test3 = async () => {
        return await client.post('/api/어쩌고주소')
    }

    /**
     * ㅇㅇㅇ페이지 :: ㅁㅁㅁ 삭제
     * @param title
     */
    test4 = async (title:string) => {
        return await client.post('/api/어쩌고주소', title)
    }
}

export const myPageApi = new MyPageApi();

 

이렇게 모아두면 설령 api path가 변경되는 일이 있더라도, 한번에 처리하기 쉽다!

실제 페이지 코드에서는 높은 가독성까지!

 

이걸 실제 코드에 적용시키면

import {NextPageWithLayout} from "@/pages/_app";
import Layout from "@/components/layout/Layout";
import {useEffect, useState} from "react";
import {myPageApi} from "@/api/myPage/myPageApi";

interface itemListProps {
    id: string
    title: string
}

const MyPage: NextPageWithLayout = () => {

    useEffect(() => {
      myPageApi.test4('test')
          .then((response:itemListProps) => {
              console.log(response)
          })
          .catch((error) => {
              console.log(error)
          })
    },[])

    return(
        <div> mypage </div>
    )
}

MyPage.getLayout = function getLayout(page) {
    return <Layout>{page}</Layout>
}

export default MyPage

 

파라미터만 url 별도입력없이 파라미터만 던지는 가독성이 깔끔한 로직이 완성된다

728x90
반응형

 

강의를 듣다보니 예전 안드로이드스튜디오랑은 좀 다른게 생겨서 메모해놓는다

empty Activity 만들고싶은데 워낙 선택지가 많아서 헷깔렸다

 

방법1. 바로선택

방법2. Gallery 보고 선택

New > Activity > Gallery 선택

 

 

Empty Views Activity 선택

 

Activity 이름 설정

이때 Activity Name 과 Layout Name을 잘 맞춰야한다

 

커서를 앞으로 이동하여 Main을 지우고 다른 문자로 바꾸면 괜찮지만, 전체삭제한경우 activity_가 prefix로 붙지않는 경우도 있기 때문이다

 

 

혹시나 activity 생성하고나서 setContentView(R.layout.레이아웃이름) 부분이 제대로 import안되더라도 걱정하지말자

일시적으로 import 인식을 못하는것같다.

 

한번 실행해보면 오류없이 실행됨!

 

728x90
반응형

TextView

html 의 p태그와 비슷하며, 텍스트를 입력할 수 있다

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="소개팅 앱!!"
 />

 

match_parent : 부모요소의 길이

예제를 기준으로, textView 상위 부모의 width 를 따라간다

 

wrap_content : 해당요소의 길이

예제를 기준으로, text 요소의 height를 입력한다

LinearLayout

여러 요소를 줄세우는 div

세로, 가로 가능

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintBottom_toBottomOf="parent"
    tools:layout_editor_absoluteX="0dp">
    
    <Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/mainColor"
    android:layout_margin="10dp"
    android:textColor="@color/white"
    android:text="Login" />

</LinearLayout>

orientation = vertical : 세로정렬

 

 

728x90
반응형

components 폴더 생성

 

root 디렉토리에 components 폴더를 생성한다

하위에 layout 폴더를 생성하고 그 안쪽에 layout과 관련된 로직을 추가할것이다

 


프로젝트가 실행될 때 가장 먼저 실행되는것이 _app.tsx 이다.
만약 localhost:3000/home 으로 접속 시 _app.tsx 가  실행되면서 Comonent로 "home" Component가 들어오게된다
그렇기에 레이아웃을 씌우는 대상은 _app.tsx가된다

 

Header, Footer 생성

 

기본적인 헤더, 푸터를 생성해보자

 

▼Header.tsx

import React from 'react'

const Header = () => {
    return (
        <>
            <h1>Header</h1>
        </>
    )
}

export default Header

 

▼Footer.tsx

import React from 'react'

const Footer = () => {
    return (
        <>
            <div>
                <h1>Footer</h1>
            </div>
        </>
    )
}

export default Footer

 

Layout 생성

 

위에서 만들어진 Header, Footer 를 layout화 시켜보자

 

▼ Layout.tsx

import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
import {Noto_Sans_KR} from "next/font/google";

type LayoutProps = {
  children: React.ReactNode
  className?: string
}

const notoSansKr = Noto_Sans_KR({
    subsets: ['latin'],
    weight: ['100', '400', '700', '900'],
    variable: '--font-notoSansKr',
})

export default function Layout({ children, className }: LayoutProps) {
  return (
    <div className={`${notoSansKr.className}`}>
      <Header />
      <div>
          {children}
      </div>
      <Footer />
    </div>
  )
}

 

layout function 에는 인자값으로 children, className 이 들어올건데, 이것의 타입은 위에 선언된 LayoutProps 이다.

그리고 Header, body, Footer 모두 폰트를 적용시키기 위하여 상위에 Classname을 설정해줬다

 

참고로 return 시 상위 태그를 선언하지않으면 오류가 난다

아래와 같이 빈 태그라도 넣어줘야한다

 

만든 Layout을 _app에 씌운다

import '@/styles/globals.css'
import type { AppProps } from 'next/app'
import {NextPage} from "next";
import {ReactElement, ReactNode} from "react";
import Head from "next/head";

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (_page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout
}
export default function App({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout ?? ((page) => page)

  return(
      <>
        {getLayout(<Component {...pageProps} />)}
      </>
  )
}

 

index 정리

 

필요없는 코드를 정리해준다

import { Inter } from 'next/font/google'
import {NextPageWithLayout} from "@/pages/_app";
import Layout from "@/components/layout/Layout";

const inter = Inter({ subsets: ['latin'] })

const Home : NextPageWithLayout = () => {
  return (
    <>
      <h1>본문 영역입니다</h1>
    </>
  )
}

Home.getLayout = function getLayout(page) {
  return<Layout>{page}</Layout>
}

export default Home

 

 

이렇게 레이아웃이 나뉘어졌다!

728x90
반응형

 

에뮬을 실행했더니 아래와 같은 오류가 확인되었다

 

Waiting For debugger

Application [프로젝트명] (process com.example.폴더명) is waiting for the debugger to attach.

 

Error running 'app'

Processes com.example.sogating are not found. Aborting session.

 

코틀린 안드로이드 개발은 처음인데.. java 처럼 오류메세지가 와락 뜨는게 아니라서 디버깅하는 방법을 공부하는중..

 

일단 에뮬이 냅다 꺼진다면, 로직이 잘못된것이므로 코드를 하나씩 지워가면서 오류포인트를 찾았다.

뭐 설정이고 무슨 문제가 아니라, 로직문제가 없는지 확인할것..!

728x90

+ Recent posts