기상청 공공 API 쓰는 법

  1. 행정안전부 공공데이터 포털 접속
    https://www.data.go.kr/
  2. 기상청 단기예보 조회서비스 검색, 오픈 API 목록에 있다
    https://www.data.go.kr/data/15084084/openapi.do
  3. 활용 신청 및 승인
    1. 로그인 / 회원가입
    2. API 활용 사유만 쓰면 활용신청 가능, 조금 기다리면 바로 승인됨
  4. 엔드 포인트와 인증키 메모
  5. API 호출 함수 생성
    fetchWeather() 함수를 만들었다
  6. 날씨 데이터를 표시할 모달 컴포넌트 생성
  7. 아이콘 클릭시 API 호출 및 모달 표시

현재 위치에 따라 날씨 데이터 가져오기

아래는 내 프로젝트에서 사용된 WeatherModal 컴포넌트 코드로 리액트, 타입스크립트, 테일윈드를 사용하고 있다

주석에 설명을 추가하였다

import React, { useEffect, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
  faTimes,
  faMapMarkerAlt,
  faThermometerHalf,
  faTint,
  faWind,
} from "@fortawesome/free-solid-svg-icons";

interface WeatherModalProps {
  isOpen: boolean;
  onClose: () => void;
}

const WeatherModal: React.FC<WeatherModalProps> = ({ isOpen, onClose }) => {
  const [weatherData, setWeatherData] = useState<any>(null);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);
  const [location, setLocation] = useState<{ nx: number; ny: number } | null>(
    null
  );

  useEffect(() => {
    if (isOpen) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          const { latitude, longitude } = position.coords;
          const { nx, ny } = convertToGridCoords(latitude, longitude);
          setLocation({ nx, ny });
        },
        (error) => {
          console.error("Error getting location:", error);
          setError("위치를 가져올 수 없습니다.");
        }
      );
    }
  }, [isOpen]);

  useEffect(() => {
    if (isOpen && location) {
      fetchWeather(location.nx, location.ny);
    }
  }, [isOpen, location]);

  const fetchWeather = async (nx: number, ny: number) => {
    setIsLoading(true);
    setError(null);
    try {
      const apiKey =
        "vk4aWLTlPOiSCrGPCqW9+RPDwCEbd2qZroyXnqkjhfKAQM15ca8W2DHua28iBIE0OaNWNEPaaAR2suzllAcotg==";
      const encodedKey = encodeURIComponent(apiKey);

      const today = new Date(); // 오늘의 날짜 객체 생성
      const hour = today.getHours(); // 현재 시각 불러오기

      let formattedTime = hour.toString().padStart(2, "0") + "00"; // API에서 요구하는 HH00 형식으로 변환

      const formattedDate = today.toISOString().slice(0, 10).replace(/-/g, "");

      const baseUrl =
        "https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst";
      const queryParams = `numOfRows=10&pageNo=1&base_date=${formattedDate}&base_time=${formattedTime}&nx=${nx}&ny=${ny}&dataType=JSON`;

      const url = `${baseUrl}?serviceKey=${encodedKey}&${queryParams}`;

      const response = await fetch(url);
      const data = await response.json();

      if (data.response && data.response.body && data.response.body.items) {
        const items = data.response.body.items.item;
        const processedData = processWeatherData(items);
        setWeatherData(processedData);
      } else {
        throw new Error("예상치 못한 API 응답 구조");
      }
    } catch (error) {
      console.error("날씨 데이터를 가져오는 중 오류 발생:", error);
      setError(
        error instanceof Error
          ? error.message
          : "알 수 없는 오류가 발생했습니다."
      );
    } finally {
      setIsLoading(false);
    }
  };

  const processWeatherData = (items: any[]) => {
    const processed: any = {};
    items.forEach((item) => {
      switch (item.category) {
        case "T1H": // 기온
          processed.temperature = parseFloat(item.obsrValue);
          break;
        case "RN1": // 강수량
          processed.rainfall = parseFloat(item.obsrValue);
          break;
        case "REH": // 습도
          processed.humidity = parseFloat(item.obsrValue);
          break;
        case "WSD": // 풍속
          processed.windSpeed = parseFloat(item.obsrValue);
          break;
      }
    });
    return processed;
  };

	// 위치 변환 함수 : 사용자의 현재 위치(위도 및 경도)를 기상청 API가 요구하는 격자 좌표(nx, ny)로 변환하는 함수
  const convertToGridCoords = (lat: number, lon: number) => {
    return { nx: 55, ny: 127 }; // 격자 좌표로 변환하는 예시 좌표 반환
  };

  if (!isOpen) {
    return null;
  }

  return (
    <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
      <div className="bg-white p-6 rounded-lg max-w-sm w-full">
        <div className="flex justify-between items-center">
          <h2 className="text-xl font-bold">현재 날씨</h2>
          <button
            onClick={onClose}
            className="text-gray-500 hover:text-gray-700"
          >
            <FontAwesomeIcon icon={faTimes} />
          </button>
        </div>
        <FontAwesomeIcon icon={faMapMarkerAlt} className="mr-2" />
        <span>현재 위치</span>
        {isLoading ? (
          <p>날씨 데이터를 불러오는 중...</p>
        ) : error ? (
          <p className="text-red-500">오류: {error}</p>
        ) : weatherData ? (
          <div>
            <div className="flex justify-between gap-2 mt-2">
              <div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
                <FontAwesomeIcon
                  icon={faThermometerHalf}
                  className="mr-2 text-blue-500"
                />
                <span className="font-bold">{weatherData.temperature}°C</span>
              </div>
              <div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
                <FontAwesomeIcon icon={faTint} className="mr-2 text-blue-500" />
                <span className="font-bold">{weatherData.humidity}%</span>
              </div>
              <div className="flex-grow basis-0 bg-blue-100 p-3 rounded-lg">
                <FontAwesomeIcon icon={faWind} className="mr-2 text-blue-500" />
                <span className="font-bold">{weatherData.windSpeed}m/s</span>
              </div>
              {weatherData.rainfall > 0 && (
                <div className="bg-blue-100 p-3 rounded-lg">
                  <FontAwesomeIcon
                    icon={faTint}
                    className="mr-2 text-blue-500"
                  />
                  <span className="font-bold">{weatherData.rainfall} mm</span>
                </div>
              )}
            </div>
          </div>
        ) : (
          <p>날씨 데이터가 없습니다.</p>
        )}
      </div>
    </div>
  );
};

export default WeatherModal;

근데 이렇게 하면 날씨가 개떡같이 나올 것이다

현재 위치를 00시 00구 00동으로 나타내려면 역지오코딩(Reverse Geocoding)이라는 기술을 사용해야한다

그건 다음 포스팅에서...

import axios from 'axios';

프로젝트 하면서 엑시오스를 불러오려는데

다른 파일에서도 멀쩡하게 불러와지던게

보기 싫은 빨간 밑줄이 생겨버렸다

 

이런 오류는 왜 뜨는걸까?!

 

원인은 from 뒤에 있다

 

 

오류 메세지를 보면

 

Absolute imports should come before relative imports. eslint (import/first)

 

=> 절대 경로는 상대 경로보다 이전에 와야만 한다 ESLint (불러오기/ 먼저)

 

절대 경로는 'react' 나 'axios' 처럼 절대적인 경로이고

 

상대 경로는 

'../../components/molecules/TopNavBarSave/TopNavBarSave'

와 같이 현재 파일의 위치로부터 설정된 경로이다

 

 

때문에 이런 오류가 떴다면 현재 위의 상황과 같이 폴더로 된 상대 경로가

엑시오스와 같은 절대 경로보다 위에 있지는 않는지 위치를 확인해보면 된다

 

 

위치를 바꿔주자 간단히 오류가 사라졌다

 

근본적으로 왜그런지 까지는 알 수 없지만 스택오버 플로우에 의하면 ESLint 설정상 법칙인 것 같다

 

https://stackoverflow.com/questions/47734886/why-should-absolute-imports-come-before-relative-imports

 

Why should absolute imports come before relative imports?

I'm importing some resources in my Vue file. The fact that it's Vue, though, have nothing to do with my question, I believe. I import them as such: import Vue from 'vue' import { mapState, mapMut...

stackoverflow.com

It's just an coding convention to make everything cleaner.

공통된 문제 하나씩 풀어와서 서로 설명해주는 오프라인 스터디

문제 풀이 의식의 흐름

먼저 left와 right의 약수의 개수를 구해야하고, 개수가 짝수이면 더하고 홀수이면 빼야한다

 

// 석우님의 풀이
function solution(left, right) {
    
    // 주어진 숫자의 약수를 구해 반환하는 함수
    function 약수(num) {
        const result = [];
        for (let i = 1; i <= num; i++) {
            if(num % i === 0) {
                result.push(i);
            }
        }
        return result;
    }
    
    // left부터 right까지의 숫자 생성
    const 대상 = new Array(right - left + 1).fill().map((number, index) => left + index);
    
    console.log(대상)
    return 대상.reduce((acc, number) => {
        if (약수(number).length % 2 === 0) {
            acc += number;
        } else {
            acc -= number;
        }
        return acc;
    }, 0);
}
// 성진님의 풀이
function solution(left, right) {
    var answer = 0;

    function divisorNum (n){
        let num = 0; 
        for(let i=1; i<= Math.sqrt(n); i++){
            if(n % i === 0 && i === Math.sqrt(n)){
                num += 1
            }
            else if(n % i ===0){
                num += 2
            }
        }
        return num;
    }
    ///// 약수 구해주는 함수 끝          


    for(let i= left; i<= right; i++){
        if(divisorNum(i) % 2 === 0) answer += i
        else if(divisorNum(i) % 2 !== 0) answer -= i
    }
    

    return answer;
}
// 도영님의 풀이
function solution(left, right) {
    var answer = 0;
    const num = right - left + 1 //갯수
    let cnt = 0; //약수의 갯수
    
    for(let i = 0; i < num ; i++){
        for(let j = 1; j <= left; j++){
            if(left % j === 0){
                cnt++ 
            } //left에서 약수의 갯수를 계산
        }
        //약수에 갯수에 따라서 덧셈, 뺄셈을 진행
        cnt % 2 === 0 ? answer += left : answer -= left
        
        //다음 값을 탐색하기 위해 left에 1을 더하고, cnt를 0으로 초기화
        left++
        cnt = 0
    }
    return answer;
}
// 윤정님의 풀이
function solution(left,right){
            let sum = 0;
            for(i=left;i<=right;i++){
            let count=0;
            for(j=1;j<=i;j++){
                if(i%j===0){
                    count++
                }
            }
            if(count%2===0){
                sum += i;
            }else{
                sum-=i;
            }
        }
            return sum
        }
// 민승님의 풀이
function solution(left,right){
var answer = 0;
    for(let i=left;i<=right;i++){
        let count = 0;
        for(let j=1;j<=i;j++){
            if (i%j ===0){
            count++
            } 
        } count%2===0?answer+=i:answer-=i;     

    }

    return answer

 

현상황: 모종의 이유로 프로젝트 폴더 삭제했다가 다시 땡겨온 상황
npm start 에서 오류메세지가 뜸

 

sh: react-scripts: command not found

 

빠른 해결을 위한 해결방법 요약

 

  1. npm start 하는 위치 확인 엉뚱한 디렉토리에서 하고 있는게 아닌지
    (필자는 이부분이 맞았다)
  2. 아니라면 npm install
  3. 다시 npm start
  4. 안된다면 node_modules 폴더 지웠다가 npm install 해보기
  5. npm start

 

처음에 npm install 했을 때

npm audit fix --force 을 하라는 메세지도 같이 나오지만 찾아보니

오히려 이렇게 했을 때 프로젝트가 망가지는 경우도 있다고 하여 일단 후퇴.

 

그리고 혹시나 하여 src가 아닌 전체 프로젝트로 빠져나와 npm start를 해보았는데, 된다!

 

구글링한 해결방법 중 node노드 모듈 폴더를 삭제하고 다시 설치하라는 방법도 있었는데,

노드 모듈 폴더가 src 폴더 바깥에 위치해 있어서 그랬던거 아닐까 싶다 (단지 나의 추측이긴하다)

 

 
 
리액트 스타일 컴포넌트로 프로젝트 중 가상요소를 넣었어야 했다

첫번째로 시도 했을때도 구글링으로 헤메다 물어물어 겨우 했었는데,

두번째 시도때에 똑같은 방법으로 가상요소를 적용하려는데 적용이 되지 않았다

 

바로 그 원인은 요소 자신에게 주느냐, 자식요소에 주느냐에 따라서였는데,

가상요소의 대상이 어딘지에 따라 사용법이 조금씩 달랐다

 

가상요소를 적용하고자 하는 부분이 어느 위치인지 이점을 분명히 알아야 할 필요가 있다

 

1. 부모 스타일 컴포넌트 안에서 특정 자식 클래스 요소에 가상 요소 주는 법

 

첫번째 사례로는 이메일 버튼으로 묶여있는 컴포넌트 안에서

구분선을 추가하기 위해 자식 요소인 클래스들 중 하나에 가상요소를 주고 싶었다

 

<LoginForm>
    <SocialLogin>
        <SocialBtn className='kt'>카카오톡 계정으로 로그인</SocialBtn>
        <SocialBtn className="gg">구글 계정으로 로그인</SocialBtn>
        <SocialBtn className="fb">페이스북 계정으로 로그인</SocialBtn>
    </SocialLogin>
    <EmailDiv className="btnEmailLogin-Join">
        <EmailButton className="btnEmailLogin">이메일로 로그인</EmailButton>
        <EmailButton className="btnJoinLogin">회원가입</EmailButton>
    </EmailDiv>
</LoginForm>

자식 클래스에 가상요소 주는 법

    const EmailDiv = styled.div`
        display: flex;
        padding-top: 20px;
        justify-content: center;
        gap: 32px;
        .btnEmailLogin::after {
            content: '';
            background-color: #C4C4C4;
            width: 0.8px;
            height: 11px;
            position: absolute;
            margin: 5px 22px;
        }
    `;

 

이렇게 부모 스타일 컴포넌트에서 가상요소를 부여할 자식 클래스명으로 가상요소를 부여하였다

 

.자식클래스명::after, before {

    가상요소 속성 입력

}

 

결과 : 구분선 속성을 가상요소로 나타내었다

 

 

2.  스타일 컴포넌트 안에서 클래스 자기자신에 가상 요소 주는 법

 

두번째로 헤맷던 부분은 가상요소를 그안에서 주려고 했을 때였다

앞의 방법과 같이 입력했지만 동작하지 않았고,

자식요소가 아닌 클래스 자기 자신에게 가상요소를 줄때는 다른 방법으로 사용하면 된다는 것을 알게 되었다

                <ChatBoard>
                    <ChatList className="newChat">
                        <ChatProfile />
                        <ChatWrap>
                            <ChatUserName>리듬이</ChatUserName>
                            <ChatPreview>책 주세요</ChatPreview>
                        </ChatWrap>
                        <ChatDate>2022.12.25</ChatDate>
                    </ChatList>
                    <ChatList>
                        <ChatProfile />
                        <ChatWrap>
                            <ChatUserName>뽐내기 좋아하는 사자</ChatUserName>
                            <ChatPreview>모던 자바스크립트 구매원합니다</ChatPreview>
                        </ChatWrap>
                        <ChatDate>2022.12.24</ChatDate>
                    </ChatList>
                        <ChatDate>2022.12.09</ChatDate>
                    </ChatList>
                </ChatBoard>

 

여기서 newChat 이라는 클래스 자체에 가상요소를 주고 싶었다

 

const ChatList = styled.li`
    font-size: 14px;
    line-height: 18px;
    display: flex;
    flex-direction: row;
    &.newChat::after {
        content: '';
        position: absolute;
        width: 12px;
        height: 12px;
        border-radius: 6px;
        background-color: #c55854;
        top: 75px;
    }
`;

 

이렇게 클래스 명 앞에 . 이 아닌 &.으로 표기해주면 잘 나타난다

 

&.자식클래스명::after, before {

    가상요소 속성 입력

}

 

결과: 새로온 메세지 알림을 임시로 가상요소로 표현해 보았다

 

 

이렇게 가상 요소를 주고자하는 위치에 따른 사용법을 프로젝트를 통해 알게 되었다

새로운 사실을 깨우치는 느낌은 너무 즐겁다!

알고리즘 문제를 풀면서 알게된 오름차순 정렬과 내림차순 정렬

공식처럼 알고있지만 어떤 원리로 정렬되는지 궁금해 찾아보았다

 

MDN 공식문서를 살펴보자

 

compareFunction이 제공되지 않으면 요소를 문자열로 변환하고 유니 코드 코드 포인트 순서로 문자열을 비교하여 정렬됩니다.

예를 들어 "바나나"는 "체리"앞에옵니다.
숫자 정렬에서는 9가 80보다 앞에 오지만 숫자는 문자열로 변환되기 때문에 "80"은 유니 코드 순서에서 "9"앞에옵니다.

compareFunction이 제공되면 배열 요소는 compare 함수의 반환 값에 따라 정렬됩니다.
a와 b가 비교되는 두 요소라면, compareFunction(a, b)이 0보다 작은 경우 a를 b보다 낮은 색인으로 정렬합니다.
즉, a가 먼저옵니다.compareFunction(a, b)이 0을 반환하면 a와 b를 서로에 대해 변경하지 않고 모든 다른 요소에 대해 정렬합니다.
참고 : ECMAscript 표준은 이러한 동작을 보장하지 않으므로 모든 브라우저(예 : Mozilla 버전은 적어도 2003 년 이후 버전 임)가 이를 존중하지는 않습니다.

compareFunction(a, b)이 0보다 큰 경우, b를 a보다 낮은 인덱스로 소트합니다.

compareFunction(a, b)은 요소 a와 b의 특정 쌍이 두 개의 인수로 주어질 때 항상 동일한 값을 반환해야합니다.
일치하지 않는 결과가 반환되면 정렬 순서는 정의되지 않습니다.

 

...쉽게 풀어쓰면

 

기본적으로 sort() 을 했을 땐 유니코드 포인트 순서대로 정렬한다

9와 80 중 80이 더 큰 숫자이지만 숫자가 아닌 문자로 보면 9보다 8이 더 작기 때문에 80을 일단 앞에 세운다

 

우리가 원하는 오름차순을 위해선 compareFunction(비교함수) 를 넣어주자

 

const arr = [5, 9, 22, 50, 109]
배열 먼저 선언해준다

arr.sort((a,b)=>a-b);
(5) [5, 9, 22, 50, 109]
오름차순

arr.sort((a,b)=>b-a);
(5) [109, 50, 22, 9, 5]
내림차순

a-b 라는 식의 값이

0보다 작을 경우, a 가 b 앞에 오도록 정렬한다

0보다 클 경우, b가 a보다 앞에 오도록 정렬한다

0일 경우, 정렬을 변경하지 않는다

 

이는 a 와 b 의 값을 바꿨을때도 동일한 논리로 연산되어 내림차순이 완성되는 것이다!!

 

 

알고리즘 문제를 풀다보면 Math.floor를 이용할 때가 많다

그런데 parseInt를 사용해도 상관없는 경우가 많았다

그렇다면, 이 둘의 차이는 무엇일까?

 

먼저, Math.floor() 란 간단히 말해 버림이다

 

출처 : MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor

 

 MDN 공식 문서의 정의로는 주어진 숫자와 같거나 작은 정수 중 가장 큰 수를 반환한다

 

parseInt 도 마찬가지로 정수를 반환한다

 

 

MDN 공식 문서의 정의로는 문자열 인자를 파싱하여 특정 진수의 정수를 반환한다

이렇듯, parseInt 의 동작방식은 조금 다르다

 

(개인적인 생각인데, 이렇게 문자열 인자로 받고 다시 숫자로 반환하는게

숫자만 계산할때는 불필요한 움직임인가 아닌가 싶다)

 

여기서 차이점을 알 수 있다

parseInt는 문자열 인자를 파싱하기 때문에

 

console.log(parseInt('109 백구'))
109

console.log(parseInt('백구 109'))
NaN

console.log(Math.floor('109 백구'))
NaN

console.log(Math.floor('백구 109'))
NaN

 

'109 백구'과 같이 문자열과 같이 있는 값에서

숫자가 시작하는 부분부터 끝나는 부분까지 저장하여 형변환이 된다

 

반면 '백구 109' 라는 문자열이 먼저오는 값은 숫자가 먼저 나오지 않아서인지 NaN값이 나온다

 

연산속도는 Math.floor가 좀 더 빠르다

 

하지만 둘의 차이는 음수에서도 나타난다

 

console.log(Math.floor('-1.5'))
-2

console.log(parseInt('-1.5'))
-1

console.log(Math.floor('-1.1'))
-2

console.log(parseInt('-1.1'))
-1

 

Math.floor는 앞서 말했듯 숫자의 버림과 같다

 

-1.5 이든, -1.1 이든간에 소수점 자리를 버리면 숫자는 더 큰 숫자가 되어버린다

(-1이 -1.5와 -1.1보다 큰 숫자니까 '버림'이 아닌 '더함'이 되버린다)

 

그래서 더 음수쪽에 가까운 정수인 -2라는 값이 나온다

 

한편, parseInt 는 숫자열을 문자열로 변환후 정수만 도출해내므로 소수점 앞의 숫자열인 -1 만 도출해낸다

 

결론

알고리즘 문제 풀 때 양수일땐 속도가 더 빠른 Math.floor를 사용하되,

문자열과 같이 있거나 음수일땐 parseInt를 사용한다

아니다.

 

Math.floor 와 같으면서 더 빠른 연산자가 있다 ~~

~연산자 를 두개 붙인 ~~ 연산자는 Math.floor 와 똑같은 기능을하면서도 더 빠른 속도로 값을 도출해낸다

 

round, floor, ceil

또, MDN 공식문서에서 재밌는 사실을 알게 되었다

 

round 반올림
floor 내림(버림)
ceil 올림

 

이렇게 자바스크립트 연산자로 반올림, 내림(버림), 올림 하는 법까지 알아보았다

새로운 정보를 알게되어 흥미로웠다

 

 

리액트로 프로젝트를 하기전 CSS를 제외한 React 구현 연습용으로 초간단한 투두리스트를 만들어 보았다

먼저, 무엇을 만들것인지 생각해본다

 

  1. 인풋 창이 있고 버튼이 있다
  2. 값을 입력하거 추가 버튼을 누르면 아이템이 추가된다
  3. 삭제버튼을 누르면 삭제가 가능하다

 

리액트 할 때 중요한 것은 모든 것을 하나 하나 나눠서 컴포넌트화 시키는 것이다


UI 에서 무엇을 컴포넌트화 시킬지 생각해보는것은 주관적이나,

확실한것은 반복되는것을 컴포넌트화 해야한다


리액트의 매력은 바로 재활용이기 때문이다

지금 우리가 해볼 투두리스트에서 반복되는것은 투두리스트의 아이템들이다
TodoItem 이 모여있는 섹션을 투두보드 TodoBoard 라고 컴포넌트화 시키면 될 듯한다

1.인풋창과 버튼 만들기

main 태그 안에 아래의 코드를 입력한다

 

 <input type="text"/>
<button>추가</button>

 

2.인풋창에 입력한 값 읽는 방법

인풋창의 값이 변화할 때마다 알려주는 onChange Event 를 준다
모든 이벤트 핸들러는 객체 형태로 값을 받는다

 

<input type="text" onChange={(event)=>console.log(event.target.value)}/>

 

인풋창에 변화가 생기면 콘솔로그에 나타날 것이다

 

이렇게

 

인풋창에 텍스트를 입력하고 지우자, 오른쪽 콘솔로그 창에 그대로 이벤트가 출력된다

이 값을 저장햇다가 나중에 아이템으로 저장하면 된다

 

저장하기 위해서는 state 를 사용한다

state를 호출해보자

 

const [inputValue, setInputValue] = useState('');

 

초기값비어있는 문자열이다

 

inputValue 값을 input 에 넣어주고 이제 콘솔로그 자리에 인풋값을 넣어준다

 

<input
    value={inputValue}
    type="text"
    onChange={(event) => console.log(event.target.value)}
/>

 

3.버튼을 클릭하면! 호출할 함수 만들기

버튼에 onClick 이벤트를 준다

 

<button onClick={addItem}>

 

클릭을 했으니 이제 아이템을 추가하자

addItem 이라는 함수도 만들어 준다

 

어디다?! 우리가 이런 데이터들을 모아두려면

어떤 데이터 타입이 제일 어울릴까?

바로, 배열이다!

 

또다시 state를 만든다

 

const [todoList, setTodoList] = useState([]);

 

useState의 타입은 이번엔 빈문자열이 아니라 배열 [ ] 이다

 

setTodoList([...todoList,inputValue])

 

... 으로 기존의 리스트들은 보존하고 새로운 인풋밸류를 가져오게한다

 

이제 이 아이템을 보여줄 투두 보드,

4. 컴포넌트를 만들자

components 폴더를 만든 뒤 TodoBoard.jsx 파일을 생성한다

 

import React from "react";

function TodoBoard() {
  return (
    <div>
      <h1>Todo List</h1>
    </div>
  );
}
export default TodoBoard;

 

이렇게 만든 컴포넌트를 App.js 파일에서 불러온다

 

import React, { useState } from "react";
import "./App.css";
import TodoBoard from "./components/TodoBoard";

function App() {
  const [inputValue, setInputValue] = useState("");
  const [todoList, setTodoList] = useState([]);
  const addItem = () => {
    setTodoList([...todoList, inputValue]);
  };

  return (
    <main>
      <input
        value={inputValue}
        type="text"
        onChange={(event) => console.log(event.target.value)}
      />
      <button onClick={addItem}>Add</button>
      <TodoBoard />
    </main>
  );
}

export default App;

 

투두보드안에 투두아이템 컴포넌트가 오게 해야한다

 

투두 아이템 컴포넌트도 만들자

 

import React from "react";

function TodoItem() {

    return (
        <div>
            리액트 공부
        </div>
    )
}

export default TodoItem

 

5. 스타일도 살짝 넣어준다

 

App.css에 가서 투두 아이템을 냅다 꾸며준다

 

.todo-item{
    width: 300px;
    height: 50px;
    border: 1px solid lightblue;
}

 

투두아이템을 클래스 네임으로 넣어준다

(클래스는 클래스 네임으로 사용한다)

 

import React from "react";

function TodoItem() {
  return <div className="todo-item">리액트 공부</div>;
}

export default TodoItem;

 

 

이 아니고 투두리스트에 저장된 아이템들은 어떻게 보여줄 수 있을까?

App.js가 가지고 있는 todoList를 어떻게 Todo Board에 전달하느냐?

 

Props를 사용한다

 

TodoBoard.jsx에 props를 주고

function TodoBoard(props) {

App.js에서도 props를 사용해 투두 보드는 불러온다

<TodoBoard todoList={TodoBoard}/>

 

6. 할일 리스트 보여주기

 

import React from "react";
import TodoItem from "./TodoItem";

function TodoBoard(props) {
  return (
    <div>
      <h1>Todo List</h1>
      {props.todoList.map((item) => (
        <TodoItem item={item} />
      ))}
    </div>
  );
}
export default TodoBoard;

어레이 함수로 받아온다

 

import React from "react";

function TodoItem(rpops) {
  return <div className="todo-item">{props.item}</div>;
}

export default TodoItem;



출처 : 코알누(코딩알려주는누나)님의 리액트로 할일앱 만들기 유튜브 영상을 보고 공부했다

 

 

 

프로젝트 첫 단계로 HTML, CSS부터 마크업 하던 중, 마진병합현상 때문에 곤란해졌다

불과 몇개월전에 배운 개념이고 실습도 했었건만, 또 까먹어서 이번엔 포스팅한다

 

피그마 시안

 

피그마 시안 로그인 페이지다

나의 의도는 왼쪽 주황색 영역이 하얀 영역부터 시작되어 텍스트를 가운데로 밀어내는 것이었다

 

마진병합현상, 마진겹침현상 예시

 

하지만, 오른쪽 이미지의 주황색 영역이 서로 겹쳐 왼쪽과 같은 현상이 일어난 것이다

 

이렇게 마진끼리 겹치는 현상을 Margin Collapsing 마진 병합 현상 이라한다

 

기억하기론 겹침이 일어나는 요소를 블럭요소 처리 해주면 된다고 했던 것 같아

display: block;

을 해봤는데, 똑같았고

display: inline-block ;

인라인 블록은 인라인 속성때문에 옆마진이 생기지 않아

하단에 있어야 할 이메일로 로그인과 회원가입이 영역을 침범했다

 

뜬금없지만 플렉스는 어떨까?

display: flex;

오?

는 무슨

에이

겹침 현상은 똑같다

 

그래서 이쯤에서 검색을 해보기로 했다

일단 display: block ; 이 왜 안통했는지 알 수 있었다

 

마진 병합 현상의 조건

인접해 있는 블록 Block 요소끼리만 일어난다.

상하단만 해당사항이다.

 

그렇다, 나는 이 소셜로그인 부분을 <div>로 묶어 놨었다

div 태그는 블록 요소이므로 마진겸침 현상 또한 일어났던 것이다 

 

그래서 해결책은?

overflow: hidden ;

부모 요소에 오버플로우 히든을 준다!!!

 

드디어!

이렇게 다시 오버플로우 히든에 대해 자세히 공부하게 되었다

 

Overflow 오버플로우란? 영역을 초과하는 데이터를 처리하는 방식이다

overflow: hidden ;

요소의 크기보다 큰 부분을 가린다

overflow: auto ;

요소의 크기보다 큰 부분이 있을 경우 스크롤이 생성된다

스크롤 Scroll 을 생성하려면 반드시 가로,세로 값 혹은 세로(높이)가 있어야 한다

키보드 컨트롤이 불가하다

overflow: visible ;

기본 형태이다

auto, hidden 속성을 초기화 한다

 

 

리액트를 처음 사용하다 보면 자동완성이 되질 않아 일일이 타이핑을 하곤 합니다

스니펫을 깔아도 되지만, 깔지 않고도 자동완성 기능을 사용할 수 있습니다

 

  1. 하단의 언어 선택
  2. Rea까지만 쳐도 JavaScript React가 뜬다
  3. 선택
  4. 완성

+ Recent posts