작성일: 2026.5.26
📦 1. 모듈 시스템 (Export / Import)
코드를 여러 파일로 분리하여 관리하고, 필요한 파일에서 불러와 재사용하는 매커니즘
1) Named Export (이름을 지정해서 내보내기)
- 한 파일에서 여러 개의 변수나 함수를 내보낼 때 사용
- 불러올 때(import) 내보낸 이름과 정확히 똑같은 이름을 중괄호({ }) 안에 적어야 함
// math.js (내보내는 파일)
export const pi = 3.14;
export const add = (a, b) => a + b;
// main.js (가져와서 쓰는 파일)
import { pi, add } from './math.js'; // 💡 반드시 중괄호가 필요하고 이름이 같아야 함
console.log(add(1, pi));
2) Default Export (기본값으로 내보내기)
- 한 파일에서 단 하나의 핵심 객체/함수/클래스만 내보낼 때 사용
- 불러올 때 중괄호({ })가 필요 없으며, 개발자가 원하는 아무 이름으로나 바꾸어 가져올 수 있음
// user.js (내보내는 파일)
const defaultUser = { name: '학생', role: 'admin' };
export default defaultUser; // 💡 파일당 딱 한 번만 가능
// main.js (가져와서 쓰는 파일)
import MyUser from './user.js'; // 💡 중괄호 없이 자유로운 이름(MyUser)으로 가져옴
console.log(MyUser.name); // '학생'
🪓 2. 구조 분해 할당 (Destructuring Assignment)
배열이나 객체의 속성을 번거로운 과정 없이 쏙쏙 뽑아내어 곧바로 개별 변수에 할당하는 문법
1) 객체 구조 분해 할당
const student = { name: '강민준', major: 'CS', age: 20 };
// 기존 방식: student.name, student.major를 일일이 대입
// 구조 분해 할당 방식: 키(Key) 이름에 맞춰 변수가 자동으로 매핑됨
const { name, major } = student;
console.log(name, major); // '강민준', 'CS'
2) 배열 구조 분해 할당
const colors = ['red', 'green', 'blue'];
// 배열의 순서(인덱스)에 맞춰 변수에 차례대로 할당됨
const [firstColor, secondColor] = colors;
console.log(firstColor, secondColor); // 'red', 'green'
🔗 3. 옵셔널 체이닝 (Optional Chaining: ?.)
객체의 깊숙한 하위 속성을 참조할 때, 중간 단계의 객체가 존재하지 않아(null 또는 undefined) 프로그램이 뻗어버리는 치명적인 에러를 우아하게 방지하는 문법
// 연락처 정보가 없는 불완전한 데이터 객체 구조
const user = {
name: '이서연',
info: null // contact 객체가 없음
};
// ❌ 기존 방식 (에러 발생): user.info가 null이므로 .contact를 읽는 순간 에러로 프로그램 종료!
// console.log(user.info.contact);
// ⭕ 옵셔널 체이닝 방식 (안전): ?. 앞의 대상이 존재하지 않으면 에러를 내지 않고 'undefined'를 반환함
console.log(user.info?.contact); // 에러 없이 깔끔하게 'undefined' 출력!
🧩 프레임워크 vs 라이브러리 핵심 요약
두 개념의 차이는 "제어의 주도권(Inversion of Control)"이 누구에게 있느냐
- 프레임워크 (Framework): 주도권이 프레임워크에 있음. 거대한 '틀/뼈대'이며, 개발자는 규칙과 라이프사이클에 맞춰 코드를 끼워 넣어야 함(예: Spring, Vue)
- 라이브러리 (Library): 주도권이 개발자에게 있음. 개발자가 필요할 때마다 자유롭게 꺼내 쓰는 '도구 상자'이며, 전체적인 흐름을 개발자가 제어
🏗️ 2. 리액트 프로젝트 표준 디렉토리 구조 (Directory Structure)
실무 및 컴포넌트 재사용성을 고려한 정석적인 src/ 내부 디렉토리 구조 잡는 방법
my-app/
├── public/ # 빌드되지 않는 정적 자원 (favicon, robots.txt 등)
└── src/ # 실제 개발 코드가 작성되는 공간
├── assets/ # 이미지, 폰트, 아이콘 등 정적 미디어 파일 (src 내장형)
│ └── img/
│ └── placeholder.png
├── components/ # 여러 페이지에서 공통으로 재사용할 최소 단위의 컴포넌트
│ ├── common/ # 전역 공통 컴포넌트 (Input, Modal 등)
│ └── styled/ # CSS-in-JS (styled-components) 모음
│ └── Button.jsx
├── pages/ # 독립된 하나의 '화면/페이지' 단위를 구성하는 컴포넌트
│ ├── ButtonPage.jsx
│ └── CommentPage.jsx
├── styles/ # 전역 CSS 스타일시트 파일 모음
│ └── comment.css
├── App.jsx # 최상위 루트 컴포넌트 (페이지들을 조립하고 연결)
└── main.jsx # 브라우저 DOM에 리액트 코드를 주입하는 시작점 (엔트리 파일)
🖼️ 3. 리액트에서 이미지(Image)를 사용하는 방법
리액트 내부(src/)에서 이미지를 다룰 때는 단순 문자열 경로를 적으면 빌드 시 주소가 깨지므로, 모듈 시스템(import)을 이용하는 것이 정석
1) src 폴더 내부 자원 가져오기 (권장)
- 장점: 파일 오타 시 빌드 에러로 즉시 인지 가능, 자동 이미지 압축 최적화, 브라우저 캐시 무력화 해시태그 생성.
import placeholderImg from '../../assets/img/placeholder.png'; // 💡 상단에서 모듈로 import
const Comment = (props) => {
return (
<div className="wrapper">
{/* 💡 중괄호 기호 내부에 변수명으로 매핑 */}
<img src={placeholderImg} className="image" alt="프로필" />
</div>
);
};
export default Comment;
2) public 폴더 자원 가져오기 (예외적 상황)
- 이미지가 너무 많아 DB에 주소 문자열만 저장해 두고 꺼내 써야 할 때는 public/에 배치하고 루트 경로(/)로 접근
// public/img/placeholder.png 에 저장되어 있는 경우
<img src="/img/placeholder.png" className="image" />
🎨 4. 리액트 스타일링 방법 (CSS vs Styled-Components)
1) 전통적인 CSS 방식 (External Style)
- 일반적인 .css 파일을 작성하고 컴포넌트 상단에서 단순 import 문으로 결합
/* styles/comment.css */
.wrapper { border: 1px solid #ddd; padding: 10px; }
.image { width: 50px; height: 50px; }
// Comment.jsx
import '../../styles/comment.css'; // 💡 단순 import 후 className으로 사용
const Comment = () => {
return <div className="wrapper">...</div>;
};
2) 현대적인 styled-components 방식 (CSS-in-JS)
- 자바스크립트 파일 내에 완전히 독립된 CSS를 내장하여 하나의 '스타일이 박제된 컴포넌트'를 만드는 기술
- 특징: 클래스명 충돌 가능성이 0%이며, 부모가 던져준 props 조건에 따라 스타일을 실시간으로 변경하는 동적 스타일링이 매우 쉬움
// components/styled/Button.jsx
import styled from 'styled-components';
// 💡 HTML button 태그에 고유 스타일을 입힌 StyledButton 컴포넌트 생성
const StyledButton = styled.button`
background-color: #4caf50;
color: white;
padding: 15px 32px;
cursor: pointer;
`;
const Button = (props) => {
return (
// 💡 부모 컴포넌트가 전달해 준 onClick과 title 데이터를 바인딩
<StyledButton onClick={props.onClick}>{props.title}</StyledButton>
);
};
export default Button;
🔌 5. Props (Property) 데이터 전달 문법
HTML의 속성(Attribute)처럼 값을 넘겨주면 자식은 하나의 객체(props)로 묶어서 받는다
1) 부모 컴포넌트가 주는 방법 (ButtonPage.jsx)
자식 컴포넌트 이름 뒤에 속성 이름(title, onClick)을 정의하고 원하는 값이나 함수를 주입.
// 부모는 서로 다른 값(title)과 행위(onClick 함수)를 각각 주입함
<Button title="글 작성하기" onClick={(e) => saveHandler()} />
<Button title="글 목록보기" onClick={(e) => listHandler()} />
2) 자식 컴포넌트가 받아 쓰는 방법 (Button.jsx)
매개변수 자리에서 props 객체를 받아 부모가 지정한 Key 이름(props.title, props.onClick)으로 꺼 씀
const Button = (props) => {
return (
// 부모가 전달해 준 데이터를 받아 HTML 태그에 최종 바인딩
<StyledButton onClick={props.onClick}>{props.title}</StyledButton>
);
};
# 오늘의 회고
주도권의 향방에 따른 프레임워크와 라이브러리의 구조적 차이를 바탕으로 React 환경에서 컴포넌트 재사용성을 극대화하는 표준 디렉토리 구조를 설계하였으며, 클래스 오염 문제를 해결하는 styled-components와 안전한 import 기반 이미지 처리 기법을 체득하여 유기적인 웹 애플리케이션 아키텍처 구축 기반을 확립했다. 이에 더해 코드의 모듈화를 극대화하는 Export/Import 매커니즘과 복잡한 데이터를 간결하게 분해하여 다루는 구조 분해 할당 문법을 파악하고, 데이터 공백 시 발생하는 런타임 에러를 안정적으로 방지하는 옵셔널 체이닝(?.) 기법을 습득했다. 결과적으로 현대 자바스크립트 환경에서 안전성과 가독성을 모두 확보한 컴포넌트 중심의 화면 구현 패턴을 완성함으로써 실무 요구사항에 유연하게 대응할 수 있는 강력한 프론트엔드 개발 역량을 다졌다.
'LG CNS AM INSPIRE CAMP' 카테고리의 다른 글
| <LG CNS 5기> 8일차 TIL : 토이프로젝트- 리액트 라우팅 설계 및 Axios 기반 회원가입·로그인 구현 (0) | 2026.05.28 |
|---|---|
| <LG CNS 5기> 7일차 TIL : React 핵심 상태 관리와 가상 DOM 및 Hooks 매커니즘 (0) | 2026.05.27 |
| <LG CNS 5기> 5일차 TIL : 자바스크립트 제어문 및 비동기 통신(Promise / Async-Await) (0) | 2026.05.22 |
| <LG CNS 5기> 4일차 TIL : 동적 DOM 렌더링 및 이벤트 처리 (0) | 2026.05.21 |
| <LG CNS 5기> 3일차 TIL HTML5 Core 레이아웃 제어: Form, Box Model, Table, Media Query (0) | 2026.05.20 |