AUROC (Area Under the Receiver Operating Characteristic curve) 는 분류 모델의 성능을 측정하는 지표 중 하나로, 모델이 양성(Positive) 클래스와 음성(Negative) 클래스를 얼마나 잘 구분하는지를 나타냅니다. 1. AUROC 계산 방법 AUROC를 이해하려면 먼저 ROC 곡선 을 이해해야 합니다. 1 ROC 곡선 (Receiver Operating Characteristic Curve) ROC 곡선은 임계값(Threshold)을 변화시킬 때, TPR(True Positive Rate) 과 FPR(False Positive Rate) 이 어떻게 변하는지를 나타낸 그래프입니다. TPR (재현율, Recall/Sensitivity): 실제 양성 중 양성으로 맞춘 비율 $$\text{TPR} = \frac{\text{TP}}{\text{TP} + \text{FN}}$$ FPR (1 - 특이도): 실제 음성 중 양성으로 틀린 비율 $$\text{FPR} = \frac{\text{FP}}{\text{FP} + \text{TN}}$$ 모델이 예측한 점수(Score)를 기준으로 임계값을 $1.0$에서 $0.0$까지 낮추어 가며, 매 단계마다 TPR과 FPR을 계산하여 그래프를 그립니다. 2 AUROC (Area Under the Curve) AUROC는 이 ROC 곡선 아래의 면적 을 의미합니다. AUROC = 1.0: 완벽한 분류기 (모든 양성을 맞히고, 모든 음성을 정확히 구분) AUROC = 0.5: 무작위 추측 (Random Guessing, 대각선 형태) AUROC < 0.5: 모델이 거꾸로 예측하고 있음 (클래스를 반대로 분류) 2. 이상 탐지(Anomaly Detection)에서 AUROC를 쓰는 이유 이상 탐지 문제에서는 일반적인 분류 문제와 다른 두 가지 큰 특징이 있으며, 이 때문에 AUROC가 매우 중요한 지표가 됩니다. 1 데이터 불균형(Imbalance) 문제 해결 이상 탐지 데이터는 대부분 정상(Normal) 이고 이상(Anomaly) 데이터는 극히 적습니다. Accuracy(정확도)의 한계: 만약 데이터의 99%가 정상이라면, 모델이 무조건 "정상"이라고만 답해도 정확도는 99%가 나옵니다. 이는 모델이 이상치를 전혀 못 찾아내도 성능이 좋아 보이는 착시 현상을 일으킵니다. AUROC의 강점: AUROC는 클래스의 비율(Imbalance)에 영향을 받지 않고, 모델이 '정상'과 '이상' 사이의 경계(Separation) 를 얼마나 잘 구분하는지를 측정합니다. 2 임계값(Threshold)에 대한 의존성 없음 (Threshold-Independent) 이상 탐지 시스템을 운영할 때, "어느 정도 점수 이상을 이상치로 간주할 것인가?"라는 임계값을 정하는 것은 매우 어렵습니다. 어떤 시스템은 오탐(False Positive)을 줄이는 것이 중요하고, 어떤 시스템은 미검(False Negative)을 줄이는 것이 중요합니다. AUROC는 특정 임계값을 지정하지 않고, 모든 가능한 임계값에서의 성능을 종합적으로 평가 합니다. 즉, 모델 자체가 가진 "구분 능력" 그 자체를 평가하므로, 나중에 최적의 임계값을 선택할 수 있는 잠재력을 측정하는 데 적합합니다. 3 순위 지정 능력(Ranking Ability) 평가 이상 탐지는 사실상 "이 데이터가 다른 데이터에 비해 얼마나 더 이상한가?" 라는 순위를 매기는 작업입니다. AUROC는 수학적으로 "무작위로 뽑은 이상치 데이터의 점수가 무작위로 뽑은 정상 데이터의 점수보다 높을 확률" 을 의미합니다. 따라서 모델이 이상치를 상위권에, 정상을 하위권에 잘 배치(Ranking)하는지를 평가하는 데 가장 직관적인 지표입니다. 요약 지표 특징 이상 탐지에서의 적합성 Accuracy 전체 중 맞춘 비율 낮음 (데이터 불균형 시 왜곡 심함) F1-Score 정밀도와 재현율의 조화 평균 높음 (특정 임계값에서의 성능 측정) AUROC 모든 임계값에서의 구분 능력 매우 높음 (불균형 데이터 및 모델의 잠재 성능 평가에 최적) 단순한 예제를 넘어, 실제 데이터 분석 환경과 유사하게 다음과 같은 요소를 포함하여 코드를 작성해 보겠습니다. 데이터 불균형(Imbalance) 구현 : 이상 탐지 데이터처럼 정상 데이터는 많고 이상 데이터는 아주 적은 상황을 시뮬레이션합니다. 다양한 모델 비교 : 아주 성능이 좋은 모델, 중간 성능의 모델, 그리고 무작위로 예측하는 모델을 비교합니다. ROC 곡선과 PR(Precision-Recall) 곡선 동시 시각화 : 이상 탐지에서는 AUROC만큼이나 AUPRC(Area Under Precision-Recall Curve) 가 중요하므로, 두 곡선을 모두 그려 비교하겠습니다. 복합 예제 코드 (Python) import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import roc_auc_score, roc_curve, precision_recall_curve, average_precision_score # 1. 복잡한 데이터셋 생성 (Imbalanced Dataset) # 1000개의 샘플 중 오직 5%만이 이상치(1)인 상황 X, y = make_classification(n_samples=1000, n_features=20, n_clusters_per_class=1, weights=[0.95, 0.05], flip_y=0, n_classes=2, random_state=42) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y, random_state=42) # 2. 세 가지 시나리오의 예측 확률(Score) 생성 # Scenario A: 매우 강력한 모델 (실제 학습된 모델) model_best = RandomForestClassifier(n_estimators=100, random_state=42) model_best.fit(X_train, y_train) y_probs_best = model_best.predict_proba(X_test)[:, 1] # Scenario B: 성능이 다소 낮은 모델 (노이즈가 섞인 예측) y_probs_mid = np.clip(y_probs_best + np.random.normal(0, 0.2, size=len(y_probs_best)), 0, 1) # Scenario C: 무작위 예측 (Random Guessing) y_probs_random = np.random.rand(len(y_test)) # 3. 지표 계산 함수 def get_metrics(y_true, y_probs): roc_auc = roc_auc_score(y_true, y_probs) pr_auc = average_precision_score(y_true, y_probs) # AUPRC return roc_auc, pr_auc # 4. 시각화 fig, ax = plt.subplots(1, 2, figsize=(16, 6)) models = [ (y_probs_best, 'High Performance', 'darkgreen'), (y_probs_mid, 'Mediocre Performance', 'orange'), (y_probs_random, 'Random Guessing', 'gray') ] for probs, label, color in models: auc_roc, auc_pr = get_metrics(y_test, probs) # ROC Curve 계산 fpr, tpr, _ = roc_curve(y_test, probs) ax[0].plot(fpr, tpr, color=color, lw=2, label=f'{label} (AUROC = {auc_roc:.2f})') # Precision-Recall Curve 계산 precision, recall, _ = precision_recall_curve(y_test, probs) ax[1].plot(recall, precision, color=color, lw=2, label=f'{label} (AUPRC = {auc_pr:.2f})') # 그래프 스타일 설정 (ROC) ax[0].plot([0, 1], [0, 1], 'k--', lw=1) ax[0].set_xlim([0.0, 1.0]) ax[0].set_ylim([0.0, 1.05]) ax[0].set_xlabel('False Positive Rate') ax[0].set_ylabel('True Positive Rate') ax[0].set_title('ROC Curve (Discriminative Power)') ax[0].legend(loc="lower right") ax[0].grid(alpha=0.3) # 그래프 스타일 설정 (PR) ax[1].set_xlim([0.0, 1.0]) ax[1].set_ylim([0.0, 1.05]) ax[1].set_xlabel('Recall') ax[1].set_ylabel('Precision') ax[1].set_title('Precision-Recall Curve (Imbalanced Data Performance)') ax[1].legend(loc="lower left") ax[1].grid(alpha=0.3) plt.tight_layout() plt.show() 💡 코드 핵심 설명 및 심화 학습 1. 왜 ROC와 PR 곡선을 같이 보나요? (매우 중요) 코드에서 두 개의 그래프를 그린 이유는 이상 탐지(Anomaly Detection)의 특수성 때문입니다. ROC Curve (왼쪽 그래프): 전체적인 모델의 구분 능력(Separation) 을 보여줍니다. 데이터가 불균형해도 (양성이 적어도) 비교적 완만하게 변화하므로, 모델의 전반적인 성능을 파악하기 좋습니다. 하지만 데이터가 극단적으로 불균형할 경우(예: 양성이 0.001%), 모델이 매우 우수함에도 불구하고 ROC 곡선이 실제보다 훨씬 좋아 보이는 낙관적 편향(Optimistic Bias) 이 발생할 수 있습니다. Precision-Recall Curve (오른쪽 그래프): 정밀도(Precision) 와 재현율(Recall) 의 관계를 보여줍니다. 이상 탐지처럼 "정상이 너무 많아서" 발생하는 오탐(False Positive)에 매우 민감하게 반응합니다. 실제 산업 현장(예: 카드 부정 결제 탐지, 불량품 검출)에서는 "정상을 이상으로 잘못 판단하는 비용"이 매우 크기 때문에, AUPRC(Area Under PR Curve) 를 더 엄격한 지표로 사용하는 경우가 많습니다. 2. 데이터 시나리오 분석 High Performance (초록색): ROC와 PR 곡선 모두 왼쪽 상단(1,1)으로 가깝게 붙습니다. 이는 양성을 아주 잘 찾아내면서도 동시에 오답을 거의 내지 않는다는 뜻입니다. Mediocre Performance (주황색): 모델의 점수에 노이즈가 섞이면 ROC는 완만하게 꺾이지만, PR 곡선은 급격하게 떨어지는 것을 볼 수 있습니다. 이는 모델이 이상치를 찾으려 할 때 정상을 이상으로 잘못 판단하는 비율이 급격히 늘어남을 의미합니다. Random Guessing (회색): ROC는 대각선($y=x$)을 따라가고, PR 곡선은 양성 클래스의 비율($\text{Positive Rate}$) 근처에서 수평선을 그립니다. 3. 결론: 어떤 지표를 믿어야 할까? 만약 당신이 "전반적으로 이 모델이 얼마나 잘 작동하는가?" 를 알고 싶다면 AUROC 를 보세요. 만약 당신이 "데이터가 매우 불균형한 상황에서, 오탐(False Positive)을 최소화하며 이상치를 얼마나 잘 잡아내는가?" 를 알고 싶다면 AUPRC 를 보세요. (이상 탐지에서는 이쪽이 훨씬 더 실무적입니다.)
카드 확장 애니메이션 시리즈 — 1편 에서 만든 걸 실제로 여러 화면에 붙여보다가 만난 버그 기록. 증상 목록 화면에서 스크롤을 한참 내린 뒤, 아래쪽에 있는 카드를 클릭해서 확대 애니메이션을 시작했다. 그런데 확대된 결과물이 스크롤로 옮겨놓은 그 위치가 아니라, 스크롤하지 않았을 때 카드가 있었을 "원래 자리"에서 튀어나왔다. 1차 가설 (틀림) 처음엔 position: fixed 가 transform 이 걸린 조상 요소 때문에 뷰포트가 아닌 엉뚱한 기준점을 잡은 게 아닐까 의심했다. 클릭 시점에 캡처한 좌표, URL로 넘겨받은 좌표, 실제로 렌더링된 위치 — 이 세 값을 디버그 로그로 찍어 비교했더니 셋 다 정확히 일치 했다. 좌표 계산 자체는 문제가 없었다는 뜻이다. 2차 가설 (맞음) 좌표 문제가 아니라면 남은 건 애니메이션 자체가 "어디서 시작하는지"를 다르게 알고 있다는 것. 여기서 원인을 찾았다. 카드에 붙인 layoutId 가 항목의 id만으로 만들어져 있었다. 즉 목록 페이지의 진짜 카드와, 상세 페이지로 넘어가면서 만든 애니메이션용 카드가 같은 layoutId 문자열 을 쓰고 있었던 것이다. Next.js 같은 프레임워크의 클라이언트 사이드 네비게이션은 브라우저를 새로고침하지 않고, 같은 JS 런타임 안에서 화면 트리만 바꾼다. 그런데 Motion(구 Framer Motion)의 layoutId 는 그 요소가 화면에서 사라진 뒤에도 한동안 "마지막으로 어디 있었는지"를 전역으로 기억 한다. 문제는 목록의 카드는 스크롤한다고 해서 다시 측정되지 않는다는 점이다(스크롤은 레이아웃 변경이 아니라서 Motion이 재계산할 이유가 없다). 그래서 Motion이 기억하고 있는 그 카드의 위치는 스크롤하기 전, 페이지를 처음 열었을 때의 자연스러운 리스트 순서상 위치 였다. 우리가 넘긴 좌표는 무시되고, Motion은 그 "오래된 기억" 위치에서부터 애니메이션을 시작해버린 것이다. 해결 카드 컴포넌트에 layoutId 를 조합할 때 접두어(prefix)를 옵션으로 받을 수 있게 만들고, 다른 화면에서 애니메이션을 시작할 땐 anchor-${item.id} 처럼 목록 페이지가 쓰는 layoutId 와 절대 겹치지 않는 문자열 을 붙였다. 이렇게 하면 Motion 입장에서는 완전히 별개의 요소로 인식해서, 엉뚱한 과거 위치를 기억해올 일이 없다. // 목록 페이지의 카드 <motion.div layoutId={`card-${item.id}`}>...</motion.div> // 다른 페이지에서 시작하는 애니메이션 (접두어로 완전히 분리) <motion.div layoutId={`anchor-${item.id}`}>...</motion.div> 왜 처음엔 안 보였나 같은 페이지 안에서만 카드를 펼쳤다 접었다 하는 시나리오에서는 이 문제가 전혀 드러나지 않는다. layoutId 가 겹치는 두 요소가 "같은 화면 안에서, 한쪽이 사라지고 다른 쪽이 나타나는" 상황에서는 오히려 그게 의도된 동작(모핑)이기 때문이다. 문제는 페이지 이동을 낀 채로 같은 layoutId 를 다시 쓸 때만 나타난다 — 그래서 단일 페이지 안의 테스트만으로는 잡히지 않고, 실제로 목록 → 상세를 오가며 확인해야 드러나는 종류의 버그였다. 일반화하면 layoutId 를 쓰는 shared layout 애니메이션을 여러 화면에 걸쳐 쓸 계획이라면, "이 id가 다른 화면의 다른 요소와 우연히 겹치지 않는가" 를 미리 점검해볼 가치가 있다. Motion 공식 문서도 비슷한 이유로 LayoutGroup 에 id 를 줘서 네임스페이스를 분리하는 방법을 안내하고 있다 — 같은 페이지 안에 같은 컴포넌트가 여러 번 렌더링될 때 layoutId 가 서로 충돌하지 않게 하기 위한 것인데, 여기서 겪은 "페이지를 넘나드는 충돌"도 결국 같은 종류의 문제(의도치 않은 layoutId 겹침)라는 점에서 원리는 같다. 출처 Motion 공식 문서, <motion /> 컴포넌트: https://motion.dev/docs/react-motion-component Motion 공식 문서, LayoutGroup (id로 layoutId 네임스페이스 분리하는 공식 패턴): https://framer.com/docs/layout-group/
ArrayList란? 패키지에 있는 크기가 자동으로 조절되는 배열 기반 리스트이다. ArrayList는 요소를 추가 시 자동으로 크기를 늘린다. ArrayList<Movie> movies = new ArrayLisr<>(); // Movie 객체를 여러개 담아두는 리스트 값 넣기 add() 값 꺼내기 get() 개수 확인 size() 삭제 remove()
지난 실습에서는 ROS 2 Service의 기본적인 개념과 명령어를 확인했다. 이번에는 앞서 확인한 Service를 실제 TurtleSim에서 호출하면서 거북이 생성, 삭제, 펜 설정 변경, 위치 이동 등의 기능을 직접 실습해 보았다. 1. /clear Service 호출 먼저 TurtleSim에 기존에 그려져 있던 선을 지우기 위해 /clear Service를 호출했다. ros2 service call /clear std_srvs/srv/Empty 실행 결과: requester: making request: std_srvs.srv.Empty_Request() response: std_srvs.srv.Empty_Response() Service 호출이 정상적으로 처리되면서 TurtleSim 화면에 그려져 있던 선이 모두 지워지는 것을 확인했다. 2. /spawn Service로 새로운 거북이 생성 다음으로 /spawn Service를 이용해 새로운 거북이를 생성했다. 이번 실습에서는 turtle3 이라는 이름의 거북이를 (8.0, 8.0) 위치에 생성했다. ros2 service call /spawn turtlesim/srv/Spawn "{x: 8.0, y: 8.0, theta: 0.0, name: 'turtle3'}" 실행 결과: requester: making request: turtlesim.srv.Spawn_Request(x=8.0, y=8.0, theta=0.0, name='turtle3') response: turtlesim.srv.Spawn_Response(name='turtle3') TurtleSim 화면에 turtle3 이 새롭게 생성되는 것을 확인할 수 있었다. 3. /kill Service로 거북이 삭제 생성한 turtle3 을 삭제하기 위해 /kill Service를 호출했다. ros2 service call /kill turtlesim/srv/Kill "{name: 'turtle3'}" Service 호출 후 TurtleSim 화면에서 turtle3 이 사라지는 것을 확인했다. 이를 통해 실행 중인 TurtleSim의 특정 거북이를 Service를 이용해 삭제할 수 있었다. 4. /turtle1/set_pen Service 실습 다음으로 turtle1 이 그리는 선의 색상과 굵기를 변경해 보았다. ros2 service call /turtle1/set_pen turtlesim/srv/SetPen "{r: 0, g: 0, b: 255, width: 8}" 여기서는 RGB 값을 이용해 펜 색상을 설정하고 width 값으로 선의 굵기를 지정했다. 이후 turtle1 을 움직여 변경된 펜 설정이 실제로 적용되는 것을 확인했다. 5. /turtle1/teleport_absolute Service 실습 이번에는 turtle1 의 위치를 직접 변경해 보았다. ros2 service call /turtle1/teleport_absolute turtlesim/srv/TeleportAbsolute "{x: 5.0, y: 5.0, theta: 0.0}" 명령어를 실행하면 turtle1 이 지정한 좌표로 바로 이동한다. 실습을 통해 거북이를 직접 조작하지 않고도 Service를 이용해 특정 위치와 방향으로 순간이동시킬 수 있다는 것을 확인했다. 6. 여러 Service를 이용한 TurtleSim 조작 이번 실습에서는 하나의 Service만 사용하는 것이 아니라 여러 Service를 직접 호출해 보았다. /clear ↓ 화면의 이동 경로 초기화 /spawn ↓ 새로운 거북이 생성 /kill ↓ 거북이 삭제 /turtle1/set_pen ↓ 펜 색상 및 굵기 변경 /turtle1/teleport_absolute ↓ 거북이 위치 및 방향 변경 각각의 Service를 호출할 때마다 TurtleSim의 상태가 실제로 변경되는 것을 확인할 수 있었다. 마무리 이번 실습에서는 ROS 2에서 제공되는 Service를 직접 호출하여 TurtleSim을 제어해 보았다. 특히 새로운 거북이를 생성하거나 삭제하고, 펜의 색상과 굵기를 변경하고, 거북이를 원하는 위치로 이동시키는 등 Service를 이용해 실행 중인 노드의 기능을 직접 요청하는 과정을 확인할 수 있었다. 이번 실습을 통해 앞서 학습한 Service가 실제 ROS 2 환경에서 어떻게 활용되는지 확인할 수 있었다.
React 왜 씀 single page application 을 편하게 만들기 위해 React 라는 자바스크립트 라이브러리 를 설치해 사용하는 것 이다. 비슷한 js라이브러리들이 많긴 하지만 가장 오래되었고 유저가 가장 많기에 리엑트를 쓴다(근-본). 장단점 장점 HTML 재사용의 편리함 컴포넌트 단위로 개발하기 때문에 여러 개발자들이 분업하기 좋음 React Native를 쓰면 리액트 비슷한 문법으로 모바일 앱개발도 가능 프론트엔드와 백엔드 파트를 완전히 분리해서 각각 개발가능 단점 많은 자바스크립트로 인해 웹페이지 크기가 커짐 웹페이지 로드시 HTML 내용물이 천천히 채워지기 때문에 SEO(Search Engine Optimization)에 악영향이 있을 수 있음 (hydration 작업 : html을 먼저보내고 자바스크립트를 연결한다) 간단한 사이트도 코드가 쓸데없이 복잡해짐 Single Page Application 말 그대로 서버에서 전체 페이지를 다시 불러오지 않고 html 한 페이지 만 써서 자바스크립트 로 현재 페이지를 동적으로 갱신한다. html 파일을 1개만 사용 다른 페이지를 보여주고 싶을 때 html 부분만 갈아치워서 보여준다 예) instagram 환경구성 nodejs 설치하면 npm 패키지 매니저가 같이 설치된다. => React 받기 편해짐 설치 후 프로젝트 경로에서 npm create vite@latest 하면 생성된다. npm run dev 개발 서버 실행한다. React 기초 문법 기초 jsx 문법 3개 프로젝트 생성시 주는 App.jsx에 작성해 보았다. function App(){ return ( <div className="App"> <div className="black-nav"> <h4>블로그임</h4> </div> </div> ) } 이런 식으로 div에 때려 넣는다 하지만 function App(){ return ( <div className="App"> <div className="black-nav"> <h4>블로그임</h4> </div> </div> <div> 안된다 </div> ) } 이건 안된다. return()안에는 하나의 div가 있어야 한다. 반환 할 때 div tag 하나만 반환하기 때문이다. import './App.css' 이런 식으로 css도 작성해서 적용이 가능하다. className 평소에는 class를 그냥 class로 넣었는데 className으로 넣어야 한다. => js에서 class를 써가지고 우리가 jsx에서 작성하고 있다는 사실을 망각하지 말자. 데이터 바인딩 문법 {} 거지같은 DOM에서 하나하나 queryseletor나 getElementById로 찾아서 수정하던 것은 잊어라 이건 jsx다. function App(){ let post = 'swap'; return ( <div className="App"> <div className="black-nav"> <div>swap</div> <div>{ post }</div> </div> </div> ) } 긴 말 필요없다. return() 밖에서 선언하고 안에 변수 명을 {}중괄호 안에 꼬라 박으면 값이 들어간다. function App(){ let post = 'swap'; return ( <div className="App"> <div className="{ post }"> <div>swap</div> <div>{ post }</div> </div> </div> ) } className에도 가능이 html에 style 속성 넣기 <div style="color : blue"> 이렇게 넣을 안일한 생각 하지마라 <div style={ {color : 'blue', fontSize : '30px'} }> 글씨 </div> 일단 - 이 기호도 못 쓰고 js에서 변수 선언할 때 마냥 대문자로 써서 붙여야 한다. 그리고 저렇게 오브젝트 형식으로 써줘야 하는데 그 이유는 JSX는 HTML이 아니라 JavaScript이기 때문이다. JSX는 빌드 시점에 결국 React.createElement() 같은 순수 JavaScript 코드로 변환된다. state 왜 씀 중요한 변수는 state에 담는다. state는 변동사항이 생기면 state쓰는 html도 자동으로 재렌더링해준다. function App(){ let post = 'swap' return ( <h4>{ post }</h4> ) } 이렇게 쓰면 코드를 더 짜야지 변경 내용이 보이는데 function App(){ let [글제목, b] = useState('swap'); return ( <h4>{ 글제목 }</h4> ) } state는 바뀌면 바로 적용 해준다. 선언 let [name, age] = ['swap', 18] array 안에 왼쪽 오른쪽 형식을 똑같이 하면 알아서 딱딱 들어가는 destructuring 문법을 사용해 선언한다. let [a,b] = useState('hello') useState()를 쓰면 [데이터1, 데이터2] 가 생기는데 데이터1 : 값 데이터2 : state변경을 도와주는 함수 가 남게 된다. state 변경 <div onclick="자바스크립트"> 일반 html 파일에선 이렇게 하면 된다. <div onClick={함수}> JSX에서는 이렇게 한다. function App(){ let [ 따봉, 따봉변경 ] = useState(0); return ( <h4> swap <span onClick={ ()=>{따봉변경(따봉+1)} } >👍</span> { 따봉 }</h4> ) } 이런 식으로 state를 변경 할 수 있다. 아까 데이터2에는 state 변경을 도와주는 함수가 들어간다 한 것을 기억한다면 이해가 어렵지 않을 것이다. function App(){ let [글제목, 글제목변경] = useState( ['남자코트 추천', '강남 우동맛집', '파이썬 독학'] ); return ( <button onClick={ ()=>{ let copy = [...글제목]; copy[0] = '여자코트 추천'; 글제목변경(copy) } }> 수정버튼 </button> ) } 또 다른 문자열 array를 갈아 끼우는 방식이다. state 자체를 변경하기에는 쓸모가 있을 수 있으니 보통 복사해서 많이 쓴다. 근데 잠깐 [...글제목] 너 뭐냐 let data1 = [1,2,3]; let data2 = data1; data2[0] = 1000; console.log(data2 === data1) true ??? 결론 부터 말하자면 data1과 data는 똑같은 값을 공유한다. 그래서 저런 괴랄한 결과가 나오는 것 이다. ... 이게 뭐냐 spread operator 라는 것 인데 ..[1,2,3] 이렇게 쓰면 그 자리에 1,2,3 이 남는다. 이렇게 하면 다른 복사본을 만들 수 있다. component 긴 HTML을 한 단어로 깔끔하게 치환해서 넣을 수 있는 문법 함수 만들듯, 변수 만들듯 HTML을 깔끔하게 한 단어로 치환해서 원하는 곳에 꽂아넣을 수 있다. 줄이는 법 function을 이용해서 함수를 하나 만들어주고 작명한다. 그 함수 안에 return () 안에 축약을 원하는 HTML을 담는다 그럼 원하는 곳에서 <함수명></함수명> 사용하면 아까 축약한 HTML이 나타난다. function App (){ return ( <div> (생략) <Modal></Modal> </div> ) } function Modal(){ return ( <div className="modal"> <h4>제목</h4> <p>날짜</p> <p>상세내용</p> </div> ) } 이런 식으로 function App (){ return ( <div> (생략) <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> <Modal></Modal> </div> ) } function Modal(){ return ( <div className="modal"> <h4>제목</h4> <p>날짜</p> <p>상세내용</p> </div> ) } 으하하 function Modal(){ return ( <div></div> ) } let Modal = () => { return ( <div></div>) } 이런 방법으로도 만들 수 있다. 주의사항 component 작명할 땐 영어대문자로 보통 작명 return () 안엔 html 태그들이 평행하게 여러개 들어갈 수 없다. function App(){} 내부에서 만들면 안된다. function App(){} 이것도 다시보니 컴포넌트 생성문법, component 안에 component 를 만들진 않는다. <컴포넌트></컴포넌트> 이렇게 써도 되고 <컴포넌트/> 이렇게 써도 된다 뭐 할때 씀 사이트에 반복해서 출현하는 HTML 덩어리들은 Component로 만들면 좋다. 내용이 매우 자주 변경될 것 같은 HTML 부분을 잘라서 Component로 만들면 좋다. 다른 페이지를 만들고 싶다면 그 페이지의 HTML 내용을 하나의 Component로 만드는게 좋다. 또는 다른 팀원과 협업할 때 웹페이지를 Component 단위로 나눠서 작업을 분배하기도 한다. 함수문법도 긴 코드 축약할 때 다른 곳에서 코드 재사용할 때 복잡한 코드를 작은 기능으로 나눌 때 컴포넌트는 그냥 함수 문법이랑 똑같아서 용도도 똑같다. 단점 관리가 힘들어질 수도 있고 안에 state 쓰기가 불편하니 필요할 때만 사용하자. 동적 UI 만들기 3step 예시로 글자 같은거 클릭시 글이 사라졌다 나타났다 하게 만들어 보자. 1. html css로 미리 UI 디자인 function Modal(){ return( <div className='modal'> <h4>제목</h4> <p>날짜</p> <p>상세내용</p> </div> ) } 예를 들어 이딴걸 만들었다 치자 2. UI의 현재 상태를 state로 저장 state 하나 만들고 거기에 현재 UI의 상태정보를 저장 let [modal, setModal] = useState(false); let [modal, setModal] = useState('닫힘'); let [modal, setModal] = useState(0); // 이런 식으로 아무렇게나 지정해도 상관없다. 3. state에 따라서 UI가 어떻게 보일지 조건문 등으로 작성 let [modal, setModal] = useState(false) <div className='list'> <h4 onClick={()=>{setModal(modal == true? false:true)}}>{글제목[2]}<span>👍</span>0</h4> <p>2월 17일 발행</p> </div> { modal == true ? <Modal/>:null } 나의 경우 조건식을 사용하여 true 일때 모달이 보이고 false라면 null로 비워두게 만들었다. map .map 이런 식으로 array 옆에 붙이는 js 기본 함수다 기능 array에 들어있는 자료갯수만큼 그 안에 있는 코드를 반복실행한다. var 어레이 = [2,3,4]; 어레이.map(function(){ console.log(1) }); 콜백함수에 파라미터 아무렇게나 작명하면 그 파라미터는 어레이 안에 있던 모든 자료를 하나씩 출력해준다. var 어레이 = [2,3,4]; 어레이.map(function(a){ console.log(a) }); return 오른쪽에 뭐 적으면 array로 담아주고 map() 쓴 자리에 남겨준다 var 어레이 = [2,3,4]; var newArray = 어레이.map(function(a){ return a * 10 }); console.log(newArray) html복사 이를 이용하면 이런 식으로 html 복사 가능하다 function App (){ return ( <div> { [1,2,3].map(function(){ return ( <div>말하면500만원</div> ) }) } </div> ) } map()의 기능들을 이용하면 다음과 같은 것들도 가능해진다 function App (){ return ( <div> (생략) { 글제목.map(function(a){ return ( <div className="list"> <h4>{ a }</h4> <p>2월 18일 발행</p> </div> ) }) } </div> ) } function App (){ return ( <div> (생략) { 글제목.map(function(a, i){ return ( <div className="list"> <h4>{ 글제목[i] }</h4> <p>2월 18일 발행</p> </div> ) }) } </div> ) } 이런 식으로 function에는 인자들을 넣어서 리스트 값들을 복사해와 하나씩 증가해 따로따로 바꿀 수도 있다. { 글제목.map(function(a, i){ return( <div className='list' key={i}> <h4>{글제목[i]}<span onClick={() => {엄지변경((이전엄지)=>{const 새엄지 = [...이전엄지];새엄지[i] += 1; return 새엄지;})}}>👍</span>{엄지[i]}</h4> <p>2월 17일 발행</p> </div> ) }) }
불변 객체 (Immutable Object) 말 그대로 한 번 만들어지면 절대 바뀌지 않는 객체이다 Primitive vs Reference Type Primityive Type(원시형) : 변수에 실제 값이 들어간다. -> 객체가 아니다 Reference Type(참조형) : 변수에 객체의 주소 (참조값)이 들어간다 레퍼런스가 레퍼런스를 불러오는경우에 값이 마음대로 바뀔 수 있음 final을추가하고 , setter를 없앰으로써 immutable 만일 final 속성을 setter로 건드리려고하면 에러가 발생한다 final 키워드 붙은걸 조작하려고 하기때문임.
이정표를 찾고 싶어서 회사를 다니면서 실제 업무를 하면서 생각보다 내 실력이 크게 늘지 않는다고 생각이 들었다. 실제로 코딩을 그리 많이 하지 않고 있고, 여러가지 지식은 생겼지만 그렇다고 그게 깊은 지식은 아니다. 내 스스로 물경력만 쌓이는 걸 방지하기 위해서 퇴근 후에 내가 하는 공부에 대해서 간단하게 기록을 하려고 한다. 목표 프로그래머스를 통한 꾸준한 문제 푸는 사고 방식을 기르자. CS 공부를 다시 시작하려고 한다. Git 내가 한 번 다뤄보고 싶었던 프로젝트를 다뤄보려고 한다. 현재까지 생각은 ROS2, DDS 등 과정 어떻게 공부를 시작할까 고민하다가 친구한테 얘기를 했는데, 서로 뜻이 맞아 친한 친구랑 스터디 모임을 만들었다. 모임이라고 해서 거창한게 아닌 그냥 퇴근하고, 집중해서 공부한 시간이 얼마나 되는지 서로 공유와 꾸준하게 목표를 지니고 해보자는 취지다. 평일 퇴근 후에 공부할 수 있는 시간은 끽해야 1~2시간이고, 많으면 4일 적어도 3일을 공부할 예정이다. 우선 이번 연도 목표에 대해서 적어보자 프로그래머스를 통한 문제 푸는 방식 기르기 소프트웨어 개발자의 사고 기르기 CS 공부 및 언어 중급 회독 (1. 운영체제 2. 전문가를 위한 C) 주저리주저리 지금 내가 하는 흐름이 날 어디까지 데려다 줄지 모른다. 하지만 지금 가만히 있으면 분명 도태되고 말 것이다. 1년도 안 된 회사 생활을 이어가면서 LLM을 통한 vLLM을 만들면서, LLM이 완벽하지 않지만 정말 무서운 도구이다. 그래서 공부를 시작하려고 한다. 지금 내가 이렇게 무지한 상태인데 더 아는게 많아면 이 도구가 성능이 얼마나 좋은건지 다른 시선에서 체감해보고 싶다. 아직 갈 길은 멀지만 시작은 미약하지만 끝은 창대하리라고 스스로 다짐한다.