Top Spring Framework Interview Questions | Baeldung
Baeldung
React, 사용자 인터페이스를 만들기 위한 자바스크립트 라이브러리 (웹 프레임워크). 싱글 페이지 애플리케이션이나 모바일 애플리케이션의 개발 시 토대로 사용될 수 있고, 사용자와 상호작용할 수 있는 동적인 UI를 쉽게 만들 수 있음
Spring 면접 전 살펴보기 위한 Q&A 35가지 (2024년 ver)
스프링 프레임워크란 무엇인가요?
자바 엔터프라이즈 애플리케이션 개발을 위한 가장 널리 사용되는 프레임워크입니다.
경량화, 제어 역전(IOC), 관점 지향 프로그래밍(AOP), 트랜잭션 관리 등의 기능을 제공합니다.
스프링을 사용하면 어떤 장점이 있나요?
경량화로 프레임워크로 인한 개발 오버헤드가 적습니다.
IoC 컨테이너가 객체 간 의존성 주입을 관리해줍니다.
AOP로 핵심 로직과 시스템 서비스를 분리할 수 있습니다.
대표적인 스프링 하위 프로젝트들은 무엇인가요?
스프링 코어: IoC/DI 등 프레임워크 핵심 기능 제공
스프링 JDBC: JDBC 코딩 없이 DB 연동 기능
스프링 ORM: JPA, Hibernate 등의 ORM 연동 계층
스프링 웹: 파일 업로드, 서블릿 리스너 등 웹 관련 기능
스프링 MVC: MVC 아키텍처 웹 개발 모듈
스프링 AOP: AOP 구현 및 메소드 인터셉터 정의
의존성 주입(DI)이란 무엇인가요?
객체를 직접 생성하지 않고, 생성 방법을 기술하면 IoC 컨테이너가 필요할 때 인스턴스화해서 주입하는 개념입니다.
스프링에서 빈을 주입하는 방식에는 어떤 것들이 있나요?
Setter 주입
생성자 주입
필드 주입
XML 또는 애노테이션으로 설정 가능
빈 주입 방식 중 권장되는 방식은 무엇이고 그 이유는 무엇인가요?
필수 의존성은 생성자 주입, 선택적 의존성은 Setter 주입을 권장합니다.
생성자 주입을 사용하면 immutable 필드에 값 주입이 가능하고 테스트가 용이해집니다.
BeanFactory와 ApplicationContext의 차이점은 무엇인가요?
BeanFactory는 빈 인스턴스를 제공하고 관리하는 컨테이너 인터페이스입니다. getBean()이 호출될 때 lazy하게 빈을 생성합니다.
ApplicationContext는 BeanFactory를 상속하면서 애플리케이션의 모든 정보, 메타데이터, 빈을 담고 있는 컨테이너 인터페이스입니다. 기본적으로 애플리케이션 구동 시점에 eager하게 빈을 생성합니다.
스프링 빈이란 무엇인가요?
스프링 IoC 컨테이너에 의해 인스턴스화, 관리되는 자바 오브젝트를 말합니다.
스프링 프레임워크에서 빈의 기본 스코프는 무엇인가요?
별도 설정이 없다면 스프링 빈은 singleton 스코프로 생성됩니다.
빈의 스코프는 어떻게 지정할 수 있나요?
@Scope 애노테이션이나 XML 설정 파일에서 "scope" 속성을 사용해 지정할 수 있습니다.
스프링에서 지원하는 빈 스코프에는 다음과 같은 것들이 있습니다.
singleton
prototype
request
session
global-session
싱글톤 빈은 스레드에 안전한가요?
아니오. 싱글톤 빈 자체는 스레드 세이프하지 않습니다.
스레드 안전성은 빈의 실행 방식에 달려 있고, 싱글톤은 생성 방식에 초점을 둔 디자인 패턴입니다.
빈의 구현 코드에 따라 스레드 안전성이 결정됩니다.
스프링 빈의 생명주기는 어떻게 되나요?
빈 정의를 읽고 인스턴스화 → 의존성 주입 → 초기화 콜백 메소드 호출 → 사용 → 소멸 콜백 메소드 호출 → 빈 소멸
각 단계별로 초기화 메소드, 소멸 메소드 등을 빈에 적절히 설정해서 사용할 수 있습니다.
스프링 자바 기반 설정이란 무엇인가요?
XML 기반 설정의 대안으로, 자바 클래스와 애노테이션을 사용해 설정하는 방식입니다.
타입 세이프한 방식으로 스프링 애플리케이션 구성이 가능합니다.
하나의 프로젝트에 여러 개의 스프링 설정 파일을 사용할 수 있나요?
네, 가능합니다. 큰 프로젝트에서는 모듈화와 유지보수성을 위해 여러 개의 설정 파일 사용이 권장됩니다.
자바 기반 설정에서는 @Configuration과 @Import 애노테이션을 사용해 여러 설정 클래스를 조합할 수 있습니다.
XML 기반 설정에서는 <import> 태그를 사용해 여러 XML 파일을 조합할 수 있습니다.
스프링 시큐리티란 무엇인가요?
스프링 기반 애플리케이션의 인증과 권한 부여 등 보안 기능을 담당하는 프레임워크입니다.
스프링 시큐리티를 사용하면 인증/인가 관련 표준 로직을 작성하지 않아도 되고, CSRF 공격 등을 방어할 수 있습니다.
웹 애플리케이션에 @EnableWebSecurity 애노테이션만 붙여주면 기본적인 웹 보안 기능이 작동합니다.
스프링 부트란 무엇인가요?
스프링 기반 애플리케이션을 빠르게 개발할 수 있게 도와주는 프로젝트입니다.
단독 실행 가능한 스프링 애플리케이션을 쉽게 생성할 수 있습니다.
내장 서버, 자동 설정, starter 의존성 등으로 최소한의 설정으로 개발을 시작할 수 있습니다.
스프링 프레임워크에서 사용되는 디자인 패턴에는 어떤 것들이 있나요?
싱글톤 패턴: 싱글톤 스코프의 빈
팩토리 패턴: BeanFactory 클래스
프로토타입 패턴: 프로토타입 스코프의 빈
프록시 패턴: 스프링 AOP
템플릿 메소드 패턴: JdbcTemplate, HibernateTemplate 등
프론트 컨트롤러 패턴: 스프링 MVC의 DispatcherServlet
데이터 접근 오브젝트(DAO) 패턴: 스프링 DAO 지원
프로토타입 스코프는 어떻게 동작하나요?
프로토타입 스코프로 정의된 빈은 매번 getBean() 메소드가 호출될 때마다 새로운 오브젝트를 생성해서 반환합니다.
이는 기본인 싱글톤 스코프와 대조적입니다. 싱글톤은 IoC 컨테이너당 하나의 오브젝트만 생성합니다.
스프링 빈에서 ServletContext와 ServletConfig 객체는 어떻게 얻나요?
스프링에서 제공하는 Aware 인터페이스들을 구현하는 방법이 있습니다.
@Autowired 애노테이션을 사용해서 주입받는 방법도 가능합니다.
스프링 MVC의 컨트롤러란 무엇인가요?
스프링 MVC에서 사용자의 요청을 처리하는 컴포넌트입니다.
@Controller 애노테이션이 붙은 클래스가 컨트롤러의 역할을 합니다.
컨트롤러 클래스의 메소드는 @RequestMapping 애노테이션으로 매핑된 특정 URI를 처리합니다.
@RequestMapping 애노테이션은 어떻게 사용하나요?
요청 URL을 컨트롤러의 메소드와 매핑할 때 사용하는 애노테이션입니다.
URL 패턴 외에도 HTTP 메소드 타입, 헤더, 파라미터 등을 매핑 조건으로 지정 가능합니다.
@PathVariable을 통해 URL 템플릿 변수를 메소드 파라미터로 받을 수 있습니다.
@RequestParam을 통해 HTTP 요청 파라미터를 메소드 파라미터로 받을 수 있습니다.
스프링 JDBC의 JdbcTemplate 클래스는 무엇이고 어떻게 사용하나요?
스프링에서 JDBC 프로그래밍을 쉽게 할 수 있도록 제공하는 템플릿 클래스입니다.
리소스 생성, 해지 등의 low-level 작업을 대신 처리해줍니다.
SQLException을 스프링 DataAccessException으로 변환하는 기능도 제공합니다.
JdbcTemplate을 사용하려면 DataSource를 스프링 설정 파일에 등록해야 합니다.
스프링에서 트랜잭션은 어떻게 사용하고, 어떤 이점이 있나요?
선언적 트랜잭션(@Transactional)과 프로그래밍적 트랜잭션(TransactionTemplate) 두 가지 방식을 제공합니다.
선언적 트랜잭션은 코드 침투가 없고 AOP를 사용하기 때문에 권장됩니다.
트랜잭션 전파, 격리 수준, 읽기 전용 등을 애노테이션 속성으로 제어할 수 있습니다.
트랜잭션 경계를 메소드 단위로 설정할 수 있어서 productivity가 높아집니다.
다양한 데이터 접근 기술에 대해 일관된 트랜잭션 제어가 가능합니다.
스프링 DAO란 무엇인가요?
Data Access Object의 약자로, 데이터 접근을 추상화한 객체입니다.
스프링은 일관성 있는 DAO를 작성할 수 있도록 다양한 기능을 제공합니다.
저수준 예외를 스프링의 통일된 예외 체계로 변환해줍니다.
템플릿 클래스를 통해 boilerplate 코드를 제거할 수 있습니다.
AOP(Aspect-Oriented Programming)란 무엇인가요?
관점 지향 프로그래밍이라고 하며, 스프링의 핵심 기능 중 하나입니다.
여러 객체에 공통으로 적용될 수 있는 기능(cross-cutting concern)을 분리해서 모듈화합니다.
주로 로깅, 트랜잭션, 보안 등 인프라 레벨의 공통 기능을 구현하는데 사용합니다.
AOP를 통해 객체 간 결합도를 낮추고, 코드 재사용성을 높일 수 있습니다.
AOP에서 Aspect, Advice, Pointcut, JoinPoint란 무엇인가요?
Aspect: 여러 객체에 공통으로 적용되는 공통 관심사(cross-cutting concern)를 모듈화 한 것입니다. 트랜잭션 관리 등이 대표적입니다.
Advice: 특정 JoinPoint에서 Aspect에 의해 취해지는 조치입니다. Around, Before, After 등의 타입이 있습니다.
Pointcut: Advice가 적용될 JoinPoint를 선별하는 조건입니다. 주로 정규 표현식으로 표현합니다.
JoinPoint: Advice가 적용될 수 있는 위치입니다. 메소드 호출, 예외 발생 등이 있습니다.
AOP의 Weaving이란 무엇인가요?
Aspect를 타깃 객체에 적용해서 새로운 프록시 객체를 생성하는 과정을 말합니다.
즉, Advice를 핵심 로직 코드에 삽입하는 과정입니다.
컴파일타임, 로딩타임, 런타임에 적용할 수 있는데, 스프링 AOP는 런타임 위빙을 사용합니다.
리액티브 프로그래밍이란 무엇인가요?
데이터 흐름과 변경 전파에 중점을 둔 프로그래밍 패러다임입니다.
주요 특징으로는 논블로킹(non-blocking), 이벤트 기반(event-driven), 느슨한 결합(loosely coupled), 확장성(scalable) 등이 있습니다.
Observer 패턴을 확장해서, 데이터 스트림을 비동기적으로 처리합니다.
스프링에서는 5.0 버전부터 WebFlux를 통해 리액티브 프로그래밍을 지원하고 있습니다.
스프링 WebFlux란 무엇인가요?
스프링 5.0에서 추가된 리액티브 웹 프레임워크입니다.
기존의 스프링 MVC와는 별개의 모듈로서 완전한 논블로킹 방식으로 동작합니다.
네티(Netty)를 기반으로 동작하며, 서블릿 컨테이너에서도 동작 가능합니다.
함수형 프로그래밍 방식을 지원하며, Mono와 Flux 타입을 사용해 리액티브 데이터 타입을 표현합니다.
Mono와 Flux란 무엇인가요?
스프링 5의 리액티브 프로그래밍에서 사용되는 핵심 객체 타입입니다.
Mono는 0-1개의 데이터를 발행하는 Reactive Stream 구현체입니다.
Flux는 0-N개의 데이터를 발행하는 Reactive Stream 구현체입니다.
모두 Reactive Stream 표준 인터페이스인 Publisher를 구현하고 있습니다.
WebClient와 WebTestClient는 각각 어떤 용도인가요?
WebClient는 스프링 WebFlux에서 제공하는 논블로킹 방식의 리액티브 HTTP 클라이언트입니다.
HTTP 요청을 비동기적으로 처리하며, Mono나 Flux를 사용해 응답을 받을 수 있습니다.
WebTestClient는 WebFlux 애플리케이션을 테스트할 때 사용하는 클라이언트입니다.
실제 서버를 띄우지 않고도 mock request/response를 사용해 컨트롤러 테스트를 할 수 있습니다.
리액티브 스트림 사용 시 주의할 점은 무엇인가요?
리액티브 프로그래밍은 기존 방식과 사고의 전환이 필요해 진입장벽이 높습니다.
리액티브 스트림을 디버깅하기 쉽지 않기 때문에 디버깅 방식에 대한 학습이 필요합니다.
전통적인 JDBC 기반 DB 드라이버는 리액티브 방식을 지원하지 않아 사용에 주의가 필요합니다.
스프링 5에서 리액티브 프로그래밍을 지원하기 위해 최소 자바 버전이 올라갔나요?
네, 맞습니다. 스프링 5는 자바 8 이상에서만 동작합니다.
자바 8의 람다, Stream API, CompletableFuture 등을 활용하기 위해 최소 사양을 올린 것으로 보입니다.
스프링 5는 자바 9의 모듈 시스템(Jigsaw)을 어떻게 지원하나요?
스프링 5의 프레임워크 라이브러리들은 자바 9 모듈 시스템을 따르도록 변경되었습니다.
그래서 필요한 모듈만 선택적으로 가져올 수 있게 되었습니다.
하지만 스프링 부트 2.0에서는 아직 자바 9 모듈을 완벽하게 지원하지 않습니다.
스프링 MVC와 WebFlux를 함께 사용할 수 있나요?
스프링 부트에서는 현재 둘 중 하나만 선택해서 사용하는 것을 권장합니다.
왜냐하면 둘 다 사용하면 자동 설정이 제대로 동작하지 않기 때문입니다.
또한 스프링 MVC는 서블릿 기반인 반면 WebFlux는 네티 기반이라 함께 사용하기에 적절치 않습니다.
출처 : https://www.baeldung.com/spring-interview-questions
📌 프론트엔드 개발자가 구독하는 메일링 서비스
제가 평소에 구독하고 있는 메일링 서비스를 소개드려요!
https://javascriptweekly.com/
매주 금요일, 자바스크립트 관련 소식을 받아볼 수 있어요.
2️⃣ Frontend Focus
https://frontendfoc.us/
매주 목요일, 프론트엔드 관련 소식을 받아볼 수 있어요. 최신 프레임워크, 라이브러리에 대한 정보를 얻을 수 있어요.
3️⃣ React Status
https://react.statuscode.com/
매주 목요일, React와 ReactNative 관련 소식을 받아볼 수 있어요. React 개발자라면 한 번쯤 알아두면 좋을 정보를 얻을 수 있어요.
4️⃣ Korean FE article
https://kofearticle.substack.com/about
매주 해외 아티클을 한국어로 번역해서 소개해요. 외국 커뮤니티에서 인기 있었던 글을 한국어로 번역해주는 경우가 많아서 트렌트를 파악하기 좋아요.
5️⃣ GeekNews Weekly
https://news.hada.io/weekly
매주 월요일, 일주일간의 GeekNews 중 엄선한 뉴스를 소개해요. 제목만 요약만 보고도 IT 업계와 기술의 흐름을 놓치지 않도록 도와줘요.
6️⃣ 일일일
https://oneoneone.kr/
매일 원하는 주제의 컨텐츠를 소개해요. 저는 개발 주제를 구독하고 있어요. 국내 기술 블로그나 커뮤니티에서 인기 있었던 글을 확인할 수 있어요.
7️⃣ 언섹시 비즈니스
https://maily.so/unsexybusinesskr
화려하지 않은 비즈니스들을 소개해요. 사이드 프로젝트 아이디어를 얻기에 좋아요.
8️⃣ 어피티 머니레터
https://uppity.co.kr/
경제 뉴스, 재테크 팁을 소개해요. 개발자에도 필요한 최소한의 금융 지식을 얻기 위해 구독하고 있어요.
당신은 개발자가 아니라 Frameworker 입니다.
Spring Framework 없이 자바로 서버 개발하기, 장고 없이 Python 백엔드 만들기, React 없이, Tailwind 없이 프론트 화면 만들기. 상상해본적 있으신가요?
요즘 개발은 확장성은 쉬워진 듯 하면서도 프레임워크 없이 단순하게 만들기 그 자체는 오히려 어려워 진 것 같습니다. 모던하게 만든다! 라고 생각했을때 단순한 CRUD 어플리케이션 개발을 위해 그냥 떠오르는 필요한 Framework, 모듈만 5개 이상은 사용하는 것 같습니다. 혹시 단순 SQL 쿼리 작성할때는 헷갈리지만 ORM으로는 뚝딱 만들고, javascript 함수의 기본은 잘 모르지만 next.js 로 어떻게 짜면 되는지 부터 찾아보진 않으셨나요?
물론 실용적인 프레임워크 사용법을 빠르게 익히는 것도 중요하지만, 그 기반이 되는 원리를 이해하고 단순하게 구현할 수도 있는 것을 알고 천천히 달려가는 것과 모르고 앞으로 달리는 것은 분명 다를것입니다. 그리고 생각보다 그 이해가 엄청나게 어렵지도 않고 오히려 더 재밌고 더 멀리 나아가는데 큰 도움이 될 수 있을 것입니다.
https://dev.to/themuneebh/youre-not-a-software-engineer-youre-a-frameworker-3bkk
📌 React 면접 질문과 답변 모음
300개 이상의 React 면접 질문과 답변을 모아둔 저장소 입니다. 영어로 되어 있지만 번역기의 도움을 받아 공부하면 면접 준비에 큰 도움이 될 것 같네요!
https://github.com/sudheerj/reactjs-interview-questions
React 면접 전 살펴보기 위한 Q&A 40가지 (2024년 ver)
React란 무엇인가요?
React는 사용자 인터페이스를 구축하기 위한 JavaScript 라이브러리입니다.
재사용 가능한 UI 컴포넌트를 생성하는 데 사용됩니다. 페이스북에서 개발하고 관리합니다.
React를 사용하는 이유는 무엇인가요?
동적 애플리케이션 쉽게 만들 수 있음: React를 사용하면 적은 코딩으로 더 많은 기능을 제공하여 동적 웹 애플리케이션을 쉽게 만들 수 있습니다.
성능 향상: React는 가상 DOM을 사용하여 웹 애플리케이션의 성능을 높입니다.
재사용 가능한 컴포넌트: 컴포넌트는 React 애플리케이션의 구성 요소로, 애플리케이션 전체에서 재사용할 수 있어 개발 시간을 크게 단축할 수 있습니다.
단방향 데이터 흐름: React는 단방향 데이터 흐름을 따르므로 디버깅이 쉽고 애플리케이션의 오류 위치를 파악할 수 있습니다.
쉬운 디버깅: 페이스북이 발표한 크롬 확장 프로그램을 사용하여 React 애플리케이션을 디버깅할 수 있어 프로세스가 빠르고 쉬워집니다.
React의 동작 방식은 어떻게 되나요?
React는 컴포넌트 기반으로 동작합니다. 컴포넌트는 애플리케이션의 독립적이고 재사용 가능한 코드 조각입니다. 각 컴포넌트는 자체 state와 props를 가지고 있습니다. React는 가상 DOM을 사용하여 변경된 부분만 실제 DOM에 반영합니다. 이를 통해 빠른 렌더링 속도를 보장합니다.
React의 주요 기능은 무엇인가요?
JSX: JSX는 JavaScript 문법 확장으로 React와 함께 사용되어 사용자 인터페이스의 모양을 설명합니다.
컴포넌트: 컴포넌트는 React 애플리케이션의 구성 요소이며, 사용자 인터페이스를 독립적이고 재사용 가능한 부분으로 분할합니다.
가상 DOM: React는 메모리에 실제 DOM의 경량화된 표현인 가상 DOM을 유지 관리합니다. 객체의 상태가 변경되면 가상 DOM은 실제 DOM에서 해당 객체만 변경합니다.
단방향 데이터 바인딩: React의 단방향 데이터 바인딩은 모든 것을 모듈화하고 빠르게 유지합니다.
높은 성능: React는 변경된 컴포넌트만 업데이트하므로 매우 빠른 웹 애플리케이션을 만들 수 있습니다.
JSX란 무엇인가요?
JSX는 JavaScript의 구문 확장입니다. React에서 사용자 인터페이스의 모양을 설명하는 데 사용됩니다. JSX를 사용하면 JavaScript 코드가 포함된 동일한 파일에서 HTML 구조를 작성할 수 있습니다.
웹 브라우저가 JSX를 직접 읽을 수 있나요?
웹 브라우저는 JSX를 직접 읽을 수 없습니다. 웹 브라우저는 일반 JS 객체만 읽도록 구축되었기 때문에 JSX는 일반 JavaScript 객체가 아닙니다. 웹 브라우저가 JSX 파일을 읽으려면 Babel을 사용하여 일반 JavaScript 객체로 변환해야 합니다.
가상 DOM이란 무엇인가요?
DOM은 Document Object Model을 나타냅니다. DOM은 HTML 문서를 논리 트리 구조로 표현합니다. 트리의 각 분기는 노드로 끝나고 각 노드에는 객체가 포함됩니다. React는 메모리에 실제 DOM의 경량화된 표현을 유지하는데, 이를 가상 DOM이라고 합니다. 객체의 상태가 변경되면 가상 DOM은 실제 DOM에서 해당 객체만 변경합니다.
Angular와 같은 다른 프레임워크 대신 React를 사용하는 이유는 무엇인가요?
동적 애플리케이션을 쉽게 만들 수 있음
가상 DOM을 사용하여 성능 향상
재사용 가능한 컴포넌트로 개발 시간 단축
단방향 데이터 흐름으로 디버깅이 쉬움
전용 도구로 쉽게 디버깅할 수 있음
ES6 표준과 ES5 표준의 차이점은 무엇인가요?
exports vs export, require vs import 등 ES6 구문이 ES5 구문과 다른 몇 가지 사례가 있습니다.
React 앱을 어떻게 만드나요?
NodeJS를 컴퓨터에 설치합니다. React 라이브러리를 포함한 많은 JavaScript 라이브러리가 포함된 npm이 필요하기 때문입니다.
명령 프롬프트나 터미널을 사용하여 create-react-app 패키지를 설치합니다.
VS Code나 Sublime Text 같은 원하는 텍스트 편집기를 설치합니다.
React에서 이벤트란 무엇인가요?
이벤트는 키 누름, 마우스 클릭 등 사용자나 시스템이 트리거할 수 있는 동작입니다. React 이벤트는 HTML에서는 소문자 대신 camelCase를 사용하여 이름이 지정됩니다. JSX에서는 HTML에서 문자열 대신 함수를 이벤트 핸들러로 전달합니다.
React에서 이벤트는 어떻게 생성하나요?
간단하게는 JSX에서 이벤트 핸들러 지정하여 생성할 수 있습니다. 컴포넌트의 JSX 엘리먼트에 이벤트 핸들러를 지정하며, 핸들러 함수는 중괄호({})로 감싸줍니다. 직접 이벤트 핸들러 함수 정의할 수도 있는데, 컴포넌트 내부에 이벤트 핸들러 함수를 정의하고, 함수는 일반적으로 arrow function 또는 메서드로 정의됩니다. 이때, 이벤트 핸들러 함수는 이벤트 객체를 매개변수로 받습니다. 이벤트 객체를 통해 이벤트에 대한 추가 정보(예: 타겟 엘리먼트, 마우스 좌표 등)에 접근할 수 있습니다.
React에서 합성 이벤트(synthetic events)란 무엇인가요?
합성 이벤트는 서로 다른 브라우저의 네이티브 이벤트에 대한 응답을 하나의 API로 결합하여 이벤트가 브라우저 간에 일관되도록 합니다. 이를 통해 애플리케이션이 실행 중인 브라우저에 관계없이 일관된 동작을 보장합니다. 여기서 preventDefault는 합성 이벤트입니다.
React에서 리스트가 어떻게 동작하는지 설명해주세요.
React에서는 일반 JavaScript에서와 마찬가지로 리스트를 생성합니다. 리스트는 데이터를 순서대로 표시하며, map() 함수를 사용하여 리스트를 순회합니다.
리스트에서 키(keys)를 사용해야 하는 이유는 무엇인가요?
키는 리스트에서 매우 중요한데 그 이유는 다음과 같습니다:
키는 고유 식별자로 리스트에서 어떤 항목이 변경, 추가 또는 제거되었는지 식별하는 데 사용됩니다.
또한 어떤 컴포넌트가 재렌더링되어야 하는지 결정하는 데 도움이 되므로, 매번 모든 컴포넌트를 렌더링하는 대신 업데이트된 컴포넌트만 재렌더링하여 성능을 높일 수 있습니다.
React에서 코멘트는 어떻게 작성하나요?
React에서 코멘트를 작성하는 방법은 두 가지가 있습니다:
한 줄 주석: // 로 시작
여러 줄 주석: /* 로 시작해서 */ 로 끝냄
React에서 화살표 함수란 무엇이고 어떻게 사용되나요?
화살표 함수는 React에 함수를 작성하는 간단한 방법입니다. 화살표 함수를 사용할 때는 생성자 내부에서 'this'를 바인딩할 필요가 없습니다. 이는 React 콜백에서 'this' 사용으로 인한 버그를 방지합니다.
React와 React Native의 차이점은 무엇인가요?
React:
출시일: 2013년
플랫폼: 웹
HTML 사용: 가능
CSS 사용: 가능
사전 지식: JavaScript, HTML, CSS
React Native:
출시일: 2015년
플랫폼: 모바일(안드로이드, iOS)
HTML 사용: 불가능
CSS 사용: 불가능
사전 지식: React.js
React 컴포넌트란 무엇인가요?
컴포넌트는 React 애플리케이션의 구성 요소이며, 하나의 앱은 일반적으로 여러 컴포넌트로 구성됩니다. 컴포넌트는 기본적으로 사용자 인터페이스의 일부입니다. 사용자 인터페이스를 독립적이고 재사용 가능한 부분으로 나눠서 별도로 처리할 수 있습니다.
React에는 두 가지 유형의 컴포넌트가 있습니다:
함수형 컴포넌트: 자체 state가 없고 렌더 메서드만 포함하므로 stateless 컴포넌트라고도 합니다. props를 다른 컴포넌트에서 데이터로 전달받을 수 있습니다.
클래스 컴포넌트: 자체 state를 보유하고 관리할 수 있으며, JSX를 반환하는 별도의 렌더 메서드가 있습니다. state를 가질 수 있으므로 Stateful 컴포넌트라고도 합니다.
React에서 render()의 용도는 무엇인가요?
각 컴포넌트에는 render() 함수가 필요합니다. 이 함수는 컴포넌트에 표시할 HTML을 반환합니다. 둘 이상의 요소를 렌더링해야 하는 경우 모든 요소를 <div>, <form>과 같은 하나의 부모 태그 안에 넣어야 합니다.
React에서 state란 무엇인가요?
state는 컴포넌트의 데이터나 정보를 포함하는 React의 내장 객체입니다. 컴포넌트의 state는 시간이 지남에 따라 변경될 수 있으며, 변경될 때마다 컴포넌트가 다시 렌더링됩니다. state 변경은 사용자 작업이나 시스템 생성 이벤트에 대한 응답으로 발생할 수 있습니다. 컴포넌트의 동작과 렌더링 방식을 결정합니다.
React에서 state는 어떻게 구현하나요?
React에서 state는 컴포넌트 내부에서 관리되는 상태 값으로, 시간에 따라 변할 수 있는 동적인 데이터를 저장하고 관리하는 데 사용됩니다.
React 16.8 버전부터 도입된 Hooks API 중 하나인 useState
훅을 사용하여 함수형 컴포넌트에서 state를 구현할 수 있습니다. useState
훅은 state 변수와 해당 변수를 업데이트하는 setter 함수를 반환합니다.
state 값을 업데이트하려면 setter 함수를 호출해야 하며, React는 자동으로 컴포넌트를 다시 렌더링하여 업데이트된 state 값을 반영합니다.
state는 컴포넌트 내에서 필요한 데이터를 저장하고 조작하는 데 사용되며, 사용자 인터랙션, 서버 데이터, 타이머 등 다양한 요인에 의해 변경될 수 있습니다.
React에서 state를 적절히 사용하면 동적이고 인터랙티브한 사용자 인터페이스를 구현할 수 있습니다. 컴포넌트 간에 state는 공유되지 않고 각 컴포넌트 내부에서 독립적으로 관리되므로, 필요한 최소한의 데이터만 state로 관리하는 것이 좋습니다.
컴포넌트의 state는 어떻게 업데이트하나요?
내장된 'setState()' 메서드를 사용하여 컴포넌트의 state를 업데이트할 수 있습니다.
React에서 props란 무엇인가요?
Props는 Properties의 약어입니다. 속성 값을 저장하고 HTML 속성과 유사하게 작동하는 React 내장 객체입니다. Props는 한 컴포넌트에서 다른 컴포넌트로 데이터를 전달하는 방법을 제공합니다. Props는 함수에 인수가 전달되는 것과 동일한 방식으로 컴포넌트에 전달됩니다.
컴포넌트 간에 props는 어떻게 전달하나요?
부모 컴포넌트에서 자식 컴포넌트로 props를 전달할 때는 JSX 내에서 속성(attribute)의 형태로 전달합니다. 자식 컴포넌트에서는 함수의 매개변수를 통해 전달받은 props에 접근할 수 있습니다. 전달받은 props는 자식 컴포넌트 내에서 읽기 전용입니다. 자식 컴포넌트는 props를 직접 수정할 수 없고, 변경이 필요한 경우 부모 컴포넌트에서 state를 변경하고 새로운 props를 전달해야 합니다.
state와 props의 차이점은 무엇인가요?
state:
용도: 컴포넌트에 대한 정보 보유
변경 가능성: 변경 가능
읽기 전용: 변경될 수 있음
하위 컴포넌트: 하위 컴포넌트가 액세스할 수 없음
Stateless 컴포넌트: state를 가질 수 없음
props:
용도: 다른 컴포넌트에 데이터를 전달하는 것을 허용
변경 가능성: 변경 불가능
읽기 전용: 읽기 전용
하위 컴포넌트: 하위 컴포넌트가 액세스할 수 있음
Stateless 컴포넌트: props를 가질 수 있음
React에서 higher-order 컴포넌트란 무엇인가요?
higher-order 컴포넌트는 다른 컴포넌트의 컨테이너 역할을 합니다. 이를 통해 컴포넌트를 단순하게 유지하고 재사용성을 높일 수 있습니다. 일반적으로 여러 컴포넌트가 공통 로직을 사용해야 할 때 사용됩니다.
두 개 이상의 컴포넌트를 하나로 어떻게 임베드할 수 있나요?
React에서 두 개 이상의 컴포넌트를 하나로 임베드하는 방법은 크게 두 가지가 있습니다.
첫 번째는 부모 컴포넌트 안에 자식 컴포넌트들을 배치하는 것입니다. 부모 컴포넌트의 JSX 안에 자식 컴포넌트들을 적절히 배치하면 됩니다. 이때 자식 컴포넌트들은 props를 통해 부모로부터 필요한 데이터를 전달받을 수 있습니다.
두 번째 방법은 합성 컴포넌트(Composite Component)를 사용하는 것입니다. 합성 컴포넌트는 여러 개의 컴포넌트를 조합하여 하나의 새로운 컴포넌트를 만드는 것을 말합니다. 이때 합성 컴포넌트 내부에서 자식 컴포넌트들을 적절히 배치하고, 필요한 props를 전달해주면 됩니다.
이렇게 함으로써 코드의 재사용성을 높이고, 컴포넌트 간의 결합도를 낮출 수 있습니다. 또한 UI의 일관성을 유지하면서도 유연하게 구조를 변경할 수 있게 됩니다.
클래스 컴포넌트와 함수형 컴포넌트의 차이점은 무엇인가요?
클래스 컴포넌트:
state를 보유하고 관리할 수 있음
stateless 컴포넌트에 비해 복잡함
모든 생명주기 메서드로 작업할 수 있음
재사용할 수 있음
함수형 컴포넌트:
state를 보유하거나 관리할 수 없음
이해하기 쉬움
생명주기 메서드로 작동하지 않음
재사용할 수 없음
컴포넌트의 생명주기 메서드를 설명해주세요.
getInitialState(): 컴포넌트가 생성되기 전에 실행됩니다.
componentDidMount(): 컴포넌트가 렌더링되어 DOM에 배치될 때 실행됩니다.
shouldComponentUpdate(): 컴포넌트가 DOM에 대한 변경 사항을 결정할 때 호출되며 특정 조건에 따라 "true" 또는 "false" 값을 반환합니다.
componentDidUpdate(): 렌더링 직후에 호출됩니다.
componentWillUnmount(): 컴포넌트가 영구적으로 파괴되고 마운트 해제되기 직전에 호출됩니다.
Redux란 무엇인가요?
Redux는 애플리케이션 상태를 관리하는 데 사용되는 오픈 소스 JavaScript 라이브러리입니다. React는 사용자 인터페이스를 구축하기 위해 Redux를 사용합니다. Redux는 JavaScript 애플리케이션을 위한 예측 가능한 상태 컨테이너로, 전체 애플리케이션의 상태 관리에 사용됩니다.
Redux의 구성 요소는 무엇인가요?
Store: 애플리케이션의 상태를 보유합니다.
Action: 저장소에 대한 정보의 출처입니다.
Reducer: 저장소로 전송된 액션에 대한 응답으로 애플리케이션의 상태가 어떻게 변경되는지 지정합니다.
Flux란 무엇인가요?
Flux는 Facebook이 웹 애플리케이션을 구축하기 위해 사용하는 애플리케이션 아키텍처입니다. 클라이언트 측 애플리케이션 내에서 복잡한 데이터를 처리하는 방법이며 React 애플리케이션에서 데이터 흐름을 관리합니다. 데이터의 단일 출처(저장소)가 있으며 특정 작업을 트리거하는 것이 저장소를 업데이트하는 유일한 방법입니다. 작업은 디스패처를 호출한 다음 저장소가 트리거되고 자체 데이터에 따라 업데이트됩니다. 디스패치가 트리거되고 저장소가 업데이트되면 변경 이벤트를 내보내 뷰가 그에 따라 다시 렌더링될 수 있습니다.
Redux는 Flux와 어떻게 다른가요?
Redux:
Redux는 애플리케이션 상태를 관리하는 데 사용되는 오픈 소스 JavaScript 라이브러리
저장소의 상태는 불변
단일 저장소만 가질 수 있음
Reducer 개념 사용
Flux:
Flux는 아키텍처이며 프레임워크나 라이브러리가 아님
저장소의 상태는 변경 가능
여러 저장소를 가질 수 있음
디스패처 개념 사용
React Router란 무엇인가요?
React Router는 React 위에 구축된 라우팅 라이브러리로, React 애플리케이션에서 경로를 생성하는 데 사용됩니다. 이는 가장 자주 묻는 React 인터뷰 질문 중 하나입니다.
React Router가 필요한 이유는 무엇인가요?
일관된 구조와 동작을 유지하고 단일 페이지 웹 애플리케이션을 개발하는 데 사용됩니다.
React 애플리케이션에 여러 경로를 정의하여 단일 애플리케이션에서 여러 뷰를 구현할 수 있습니다.
React 라우팅은 기존 라우팅과 어떻게 다른가요?
React 라우팅:
단일 HTML 페이지
사용자는 동일한 파일에서 여러 뷰를 탐색함
단일 파일이므로 페이지가 새로 고쳐지지 않음
성능 향상
기존 라우팅:
각 뷰는 새로운 HTML 페이지
사용자는 각 뷰에 대해 여러 파일을 탐색함
사용자가 탐색할 때마다 페이지가 새로 고쳐짐
성능이 느림
React 라우팅은 어떻게 구현하나요?
React에서 라우팅을 구현하기 위해 일반적으로 react-router-dom 라이브러리를 사용합니다. 주요 과정은 다음과 같습니다.
react-router-dom 설치: npm이나 yarn을 통해 라이브러리를 설치합니다.
index.js에서 BrowserRouter로 App 컴포넌트 감싸기: BrowserRouter는 HTML5의 History API를 사용하여 UI를 URL과 동기화합니다.
App.js에서 Route 컴포넌트를 사용하여 경로 설정: Route 컴포넌트의 path prop에 경로를, component prop에는 렌더링할 컴포넌트를 지정합니다.
Link 컴포넌트를 사용하여 다른 경로로 이동할 수 있는 링크 생성: Link 컴포넌트의 to prop에 이동할 경로를 설정합니다.
필요한 경우 파라미터나 쿼리 문자열을 사용하여 동적 라우팅 구현: URL 파라미터는 Route 경로에 콜론(:)을 사용하여 설정하고, useParams 훅으로 접근합니다. 쿼리 문자열은 location 객체의 search 속성을 통해 접근할 수 있습니다.
React에서 CSS 모듈을 사용하는 방법을 설명해주세요.
CSS 모듈 파일은 .module.css 확장자로 생성됩니다.
모듈 파일 내부의 CSS는 해당 모듈을 가져온 컴포넌트에서만 사용할 수 있으므로, 컴포넌트 스타일링 시 이름 충돌이 발생하지 않습니다.
React 컴포넌트는 어떻게 스타일링하나요?
React 컴포넌트를 스타일링하는 몇 가지 방법이 있습니다:
인라인 스타일링
JavaScript 객체
CSS 스타일시트
출처 : https://www.simplilearn.com/tutorials/reactjs-tutorial/reactjs-interview-questions
시뮬레이션을 실행하려고 해도 스크립트 문제, iPhone 버전 범위 문제, 시뮬레이터 문제가 계속 발생합니다. 어떤 도움이라도 감사합니다. ReactNative를 처음 접했습니다. 저희 팀에서 저를 도울 수 있는 사람이 없습니다. #프로젝트 환경 mac M2 ruby -v ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin23] node -v v20.10.0 pod --version 1.15.2 package.json { "name": "labts", "version": "0.0.1", "private": true, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "lint": "eslint .", "start": "react-native start", "test": "jest" }, "dependencies": { "@react-native-community/async-storage": "^1.12.1", "@react-native-community/cli": "13.6.9", "@react-navigation/bottom-tabs": "^6.6.0", "@react-navigation/native": "^6.1.17", "@react-navigation/native-stack": "^6.10.0", "@tanstack/react-query": "^5.51.5", "@types/react-native-vector-icons": "^6.4.18", "axios": "^1.7.2", "date-fns": "^3.6.0", "immer": "^10.1.1", "react": "18.2.0", "react-native": "0.74.3", "react-native-calendars": "^1.1305.0", "react-native-date-picker": "^5.0.4", "react-native-dotenv": "^3.4.11", "react-native-get-random-values": "^1.11.0", "react-native-image-crop-picker": "^0.41.2", "react-native-image-zoom-viewer": "^3.0.1", "react-native-paper": "^5.12.3", "react-native-permissions": "^4.1.5", "react-native-safe-area-context": "^4.10.8", "react-native-screens": "^3.32.0", "react-native-splash-screen": "^3.3.0", "react-native-tab-view": "^3.5.2", "react-native-vector-icons": "^10.1.0", "react-native-vision-camera": "^4.5.1", "uuid": "^10.0.0", "yarn": "^1.22.22" }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", "@react-native/babel-preset": "0.74.85", "@react-native/eslint-config": "0.74.85", "@react-native/metro-config": "0.74.85", "@react-native/typescript-config": "0.74.85", "@types/react": "^18.2.6", "@types/react-native-dotenv": "^0.2.2", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.6.3", "babel-plugin-module-resolver": "^5.0.2", "eslint": "^8.19.0", "jest": "^29.6.3", "prettier": "2.8.8", "react-test-renderer": "18.2.0", "typescript": "5.0.4" }, "engines": { "node": ">=18" } } PodFile require Pod::Executable.execute_command('node', ['-p', 'require.resolve( "react-native/scripts/react_native_pods.rb", {paths: [process.argv[1]]}, )', __dir__]).strip platform :ios, '12.0' use_frameworks! #use_modular_headers! prepare_react_native_project! linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green use_frameworks! :linkage => linkage.to_sym end target 'nexlabts' do config = use_native_modules! use_react_native!( :path => config[:reactNativePath], # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) target 'nexlabtsTests' do inherit! :complete # Pods for testing end post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, config[:reactNativePath], :mac_catalyst_enabled => false, # :ccache_enabled => true ) end end 제가 아래 에러 3가지에 대해 제가 해본 방법들입니다. 1. node 재설치 node_module 폴더 삭제, package-rock.json 삭제 후 재설치 npm install --legacy-peer-deps yarn install 2. Xcode가 node 읽을 수 있도록 설정 sudo ln -s "$(which node)" /usr/local/bin/node 3. Podfile 내 platform 설정 수정 platform :ios, '12.0' or platform :ios, '14.0' 4. Pods 재설치 rm -rf ~/Library/Developer/Xcode/DerivedData or rm -rf ~/Library/Developer/Xcode/DerivedData/* rm -rf Pods rm Podfile.lock pod install --repo-update Xcode \> Product \> Clean Build Folder. cd ./ios pod cache clean -all pod install --repo-update cd ../ npx react-native run-ios --no-packager --simulator="iPhone 15" or npx react-native run-ios --simulator="iPhone 15" or yarn start > i(run ios) Err 1. cocoaPods 설치할 때 [!] CocoaPods could not find compatible versions for pod "React-RuntimeHermes": In Podfile: React-RuntimeHermes (from ../node_modules/react-native/ReactCommon/react/runtime) Specs satisfying the React-RuntimeHermes (from ../node_modules/react-native/ReactCommon/react/runtime) dependency were found, but they required a higher minimum deployment target. Err2. iOS 실행할때 run-ios --no-packager --simulator="iPhone 15" Build description signature: fc1341421f84b87c5245d346c2c17b66 Build description path: /Users/nowonjae/Library/Developer/Xcode/DerivedData/nexlabts-argvodqcybjfcybstpulfpghnzvm/Build/Intermediates.noindex/XCBuildData/fc1341421f84b87c5245d346c2c17b66.xcbuilddata /Users/nowonjae/Desktop/project/NeXLabRN/ios/nexlabts.xcodeproj:1:1: error: Unable to open base configuration reference file '/Users/nowonjae/Desktop/project/NeXLabRN/ios/Pods/Target Support Files/Pods-nexlabts/Pods-nexlabts.release.xcconfig'. (in target 'nexlabts' from project 'nexlabts') warning: Unable to read contents of XCFileList '/Target Support Files/Pods-nexlabts/Pods-nexlabts-resources-Release-output-files.xcfilelist' (in target 'nexlabts' from project 'nexlabts') warning: Unable to read contents of XCFileList '/Target Support Files/Pods-nexlabts/Pods-nexlabts-frameworks-Release-output-files.xcfilelist' (in target 'nexlabts' from project 'nexlabts') error: Unable to load contents of file list: '/Target Support Files/Pods-nexlabts/Pods-nexlabts-frameworks-Release-input-files.xcfilelist' (in target 'nexlabts' from project 'nexlabts') error: Unable to load contents of file list: '/Target Support Files/Pods-nexlabts/Pods-nexlabts-frameworks-Release-output-files.xcfilelist' (in target 'nexlabts' from project 'nexlabts') warning: Run script build phase 'Bundle React Native code and images' will be run during every build because it does not specify any outputs. To address this warning, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'nexlabts' from project 'nexlabts') warning: Run script build phase '[CP] Embed Pods Frameworks' will be run during every build because it does not specify any outputs. To address this warning, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'nexlabts' from project 'nexlabts') error: Unable to load contents of file list: '/Target Support Files/Pods-nexlabts/Pods-nexlabts-resources-Release-input-files.xcfilelist' (in target 'nexlabts' from project 'nexlabts') error: Unable to load contents of file list: '/Target Support Files/Pods-nexlabts/Pods-nexlabts-resources-Release-output-files.xcfilelist' (in target 'nexlabts' from project 'nexlabts') warning: Run script build phase '[CP] Copy Pods Resources' will be run during every build because it does not specify any outputs. To address this warning, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'nexlabts' from project 'nexlabts') --- xcodebuild: WARNING: Using the first of multiple matching destinations: { platform:iOS Simulator, id:B5AA2E84-4F83-4749-A986-A1FCE5E398A3, OS:17.5, name:iPhone 15 } { platform:iOS Simulator, id:B5AA2E84-4F83-4749-A986-A1FCE5E398A3, OS:17.5, name:iPhone 15 } ** BUILD FAILED ** ] Err3. Xcode 로 Build 할때 (Any iOS Simulator Device (arm64, x86_64)) Command PhaseScriptExecution failed with a nonzero exit code
위 에러 해결되었습니다. 크몽에 범코딩님께 에러 문의 및 코칭 요청드렸고 피드백으로 에러 발생 원인은 중복실행 문제로. 아이폰 직접 연결 빌딩시 해결될 것으로 판단된다고 피드백 주셔서, 진행해보니 정상적으로 잘 빌딩되었습니다. 관련해서 추가 참고한 블로그입니다. https://pocket-dev.tistory.com/35#google_vignette
import React, { useEffect, useState } from "react"; import { Link, useLocation } from "react-router-dom"; import { TbArrowBigUpFilled } from "react-icons/tb"; import useScroll from "../utils/useScroll"; import useProductsQuery from "../useProductsQuery"; const Nav = () => { const { isNavFixed } = useScroll(); const [activeButton, setActiveButton] = useState(0); const handleButtonClick = (index) => { setActiveButton(index); }; const categoryNames = { materials: "소재별 품목", purposes: "용도별 품목", }; const [isScrollMenuVisible, setScrollMenuVisible] = useState(false); const handleMouseEnter = () => { setScrollMenuVisible(true); }; const handleMouseLeave = () => { // console.log("마우스 이탈"); setScrollMenuVisible(false); }; const location = useLocation(); const isloginorsignup = location.pathname === "/login" || location.pathname === "/signup"; const { isLoading, isError, errorMessage, materials, purposes } = useProductsQuery(); const [hoveredOnedepth, setHoveredOnedepth] = useState(null); const [onedepthChildren, setOnedepthChildren] = useState([]); const [hoveredDivision, setHoveredDivision] = useState(null); const [twodepthDivision, setTwodepthDivision] = useState([]); const handleOnedepthMouseEnter = (item) => { setHoveredOnedepth(item); // console.log(setHoveredOnedepth) setOnedepthChildren(item.children); }; const handletwodepthMouseEnter = (child) => { setHoveredDivision(child); // console.log(setTwodepthDivision); setTwodepthDivision(child.divisions); // console.log(child.divisions); }; if (isLoading) return <>로딩 중...</>; if (isError) return <>{errorMessage}</>; return ( <> {!isloginorsignup ? ( <> {/* 대메뉴 */} <nav id="nav" className={`nav ${isNavFixed ? "fixed" : ""}`}> <div className="nav_wrap"> <div className="twobutton_wrap"> {["materials", "purposes"].map((category, index) => ( <Link type="button" className={`twobutton ${ activeButton === index ? "active" : "" }`} key={index} onClick={() => handleButtonClick(index)} to={`/${category}`} > {categoryNames[category]} </Link> ))} </div> {/* 내비게이션 1depth */} <div className="scrollnav"> <ul className="materialnav" style={{ display: activeButton === 0 ? "flex" : "none", }} > {materials.map((material) => ( <li key={material.id} className="materialnav_li" onMouseEnter={() => { handleOnedepthMouseEnter(material); handleMouseEnter(); }} onMouseLeave={handleMouseLeave} > <Link to="/">{material.name}</Link> </li> ))} </ul> <ul className="usagenav" style={{ display: activeButton === 1 ? "flex" : "none", }} > {purposes.map((purpose) => ( <li key={purpose.id} className="usagenav_li" onMouseEnter={() => { handleOnedepthMouseEnter(purpose); handleMouseEnter(); }} onMouseLeave={handleMouseLeave} > <Link to="/">{purpose.name}</Link> </li> ))} </ul> </div> <div className="call_wrap"> <div className="call"> <div className="call_title">견적 상담 · 문의 대표전화</div> <div className="top_button"> <TbArrowBigUpFilled className="top_icon" /> <p>TOP</p> </div> </div> </div> </div> </nav> {/* 2&3depth 호버 메뉴 */} <div className={`scrollmenu ${isScrollMenuVisible ? "show" : ""} ${ isNavFixed ? "fixed" : "" }`} style={{ visibility: isScrollMenuVisible ? "visible" : "hidden" }} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > {activeButton === 0 && ( <div className="scrollmenu_top"> <ul className="depth2_wrap"> {onedepthChildren.map((child) => ( <li key={child.id} className="depth2_menu" onMouseEnter={() => { handletwodepthMouseEnter(child); }} > <div className="depth2_menu_img_wrap"> <div className="depth2_menu_img"> <img src={child.images[0]._links.href} alt={child.name} /> </div> </div> <p>{child.name}</p> </li> ))} </ul> </div> )} <div className="scrollmenu_middle"> <div className="depth3_wrap"> {twodepthDivision.map((division) => ( <Link key={division.id} to="/" className="depth3_menu"> <span className="depth3_menu_img"> {/* images 속성이 없거나 비어 있어도 오류를 방지 */} {division.images && division.images.length > 0 && ( <img src={division.images[0]._links.href} alt={division.division_name} /> )} </span> <p>{division.division_name}</p> </Link> ))} </div> </div> <div className="scrollmenu_bottom"></div> </div> </> ) : null} </> ); }; export default Nav; 현재는 depth2_menu 호버해야만 depth3_menu가 보이는데 초기에 scrollmenu가 보일때부터 depth2_menu가 (onedepthChildren.map의 첫번째 순서의 데이터가) 호버되어있어 그에 매칭되는 division의 내용들이 depth3_menu에 보이게 할수있는 방법 있을까요 ?
안녕하세요! 자세하게 코드를 남겨주신덕에 호다닥 잘 살펴보고 왔습니다ㅎㅎㅎ 현재 주신 구조에서 ScrollMenu가 보이기 시작하는 시점은 material이나 purpose를 호버하는 시점으로 보이네요! 현재 코드를 유지하는 관점에서 가장 간단한 해결법으로는, handleOnedepthMouseEnter 핸들러 내에서 setOnedepthChildren을 해줌과 동시에 if (item.children.length > 0) handletwodepthMouseEnter(item.children[0]) 를 호출해주는 방법이 있을 수 있겠네요! 질문의 의도가 맞는지는 모르겠지만, 만약 activeButton이 1(purpose)일 경우 depth2를 건너뛰는게 목적이라면, usagenav_li의 onMouseEnter에서 바로 handletwodepthMouseEnter(purpose)를 호출해주는 것도 방법일 수 있습니다. 일단 코드를 유지하는 선에서 방식을 말씀 드리긴 했지만, 저의 개인적인 욕심(?)을 아주 약간 덧붙여 몇가지 리팩토링을 진행해주신다면 조금 더 좋은 코드가 될 수 있어보입니다! 1. 내부 변수 이름 개선 - 조금 더 시멘틱하고 통일된 스타일의 변수 / 핸들러 명을 지어주시면 유지보수 면에서 깔끔할 것 같습니다 2. activeButton 개선 - 카테고리 선택시 index보다는 enum(TS 사용시), 혹은 categoryNames오브젝트를 활용하여 관리해주신다면, 추후 카테고리의 추가나 순서 변화 등에 유연하게 대처 가능한 코드가 될 것 같습니다 3. depth1의 경우, style의 display속성을 통해 변환해주시는 것으로 보입니다. 이럴 경우, 렌더 트리에는 반영되지 않지만 DOM트리에는 두 컴포넌트 모두 반영되어 리소스 낭비 및 불필요한 리렌더가 예상되므로, 조건부 렌더 방식을 고려해보시는 것도 좋아보입니다. (레퍼런스: https://ssangq.netlify.app/posts/conditional-rendering-vs-diplay-none) 4. !isloginorsignup 일 경우 그냥 null로 early return 시켜버려도 깔끔할 것 같습니다 조금 욕심 부리다보니 글이 길어졌네요,,ㅎㅎㅎ 기획 의도나 스타일에 따라 해석이 달라질 수 있으니, 혹시 추가로 궁금하신 부분이나 답변의 핀트가 엇나간 부분이 있다면 편하게 알려주세요!
안녕하세요! Next.js SSR + react-query 조합을 사용하려고 하는데요, page 컴포넌트 내 getServerSideProps 함수에서 prefetching을 받아온 후에 serializing 에러가 발생합니다. (Next.js는 13버젼입니다.) 에러 내용은 다음과 같습니다. Error: Error serializing `.dehydratedState.queries[0].state.data.headers` returned from `getServerSideProps` in "/top". Reason: `object` ("[object AxiosHeaders]") cannot be serialized as JSON. Please only return JSON serializable data types. 해당 에러 내용으로 구글링을 해보니, 대부분 getServerSideProps 함수 반환 코드에서 return { props: { dehydratedState: JSON.parse(JSON.stringify(dehydrate(queryClient))), }, }; 와 같이 dehydrate(queryClient)값을 JSON화 -> Object화를 하라고 하는데요, 이와 같이 사용해도 또 다시 아래와 같은 에러가 납니다. TypeError: Converting circular structure to JSON --> starting at object with constructor 'ClientRequest' | property 'socket' -> object with constructor 'Socket' --- property '_httpMessage' closes the circle Backend API는 Express.js를 사용하고 있으며, res.status(200).json({ data: ~ })와 같은 방식으로 응답을 주고 있습니다. 어떻게 해결할 수 있을까요? 코드 첨부가 안되네요, 아래는 page 컴포넌트가 위치한 파일의 전체 코드입니다. import type { ReactElement } from 'react'; import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query'; import { format } from 'date-fns'; import TopMusicContainer from '~containers/TopMusicContainer'; import Layout from '~layouts/Layout'; import type { NextPageWithLayout } from '~pages/_app'; import TopMusicService from '~services/topMusicService'; import * as MusicType from '~types/musicType'; export async function getServerSideProps() { const queryClient = new QueryClient(); await queryClient.prefetchQuery(['fetchTopMusic'], () => { const params: MusicType.ListRequestType = { filter: 'title', keyword: '', page: 1, limit: 25, time: format(new Date(), 'yyyyMMddHH'), }; return TopMusicService.list(params); }); return { props: { dehydratedState: JSON.parse(JSON.stringify(dehydrate(queryClient))), }, }; } const Top: NextPageWithLayout = (): JSX.Element => { const { data, isLoading } = useQuery({ queryKey: ['fetchTopMusic'], queryFn: () => { const params: MusicType.ListRequestType = { filter: 'title', keyword: '', page: 1, limit: 25, time: format(new Date(), 'yyyyMMddHH'), }; return TopMusicService.list(params); }, }); return ( <section> <TopMusicContainer /> </section> ); }; Top.getLayout = function getLayout(page: ReactElement) { return <Layout>{page}</Layout>; }; export default Top;
안녕하세요. Axios response를 그대로 사용하시면 함수 등 JSON으로 변환이 불가능한 것들이 포함되어 있어서 axios response에서 data만 추출해야 하는 것으로 알고 있는데요. 혹시 TopMusicService.list(params); 의 리턴 타입이 어떻게 되나요? 참고한 SO 포스트: - https://stackoverflow.com/questions/67204170/getserversideprops-functions-response-cannot-be-serialized-as-json-in-next-js
안녕하세요. 현재 리액트 네이티브로 프로젝트를 진행하고 있습니다. 막바지 단계에 거의 도달해서 이제 배포 준비를 하려고 하는데요, 마지막으로 테스트를 하기 위해 stagingRelease 로 apk 를 만들어서 테스트를 하려고 하니, .env.staging 을 읽지를 못하고 있네요. 이상한건 stagingDebug 로 할 때에는 이런 문제가 없었습니다. 지금 환경변수는 루트 디렉토리에 env 폴더가 있고, 해당 폴더 내에는 3개의 환경변수 파일이 존재합니다. .env.development .env.staging .env.production 이렇게 3개 입니다. project.ext.envConfigFiles = [ productiondebug: "env/.env.production", productionrelease: "env/.env.production", developmentrelease: "env/.env.development", developmentdebug: "env/.env.development", stagingrelease: "env/.env.staging", stagingdebug: "env/.env.staging" ] 위의 코드는 제가 설정한 환경변수 매핑하는 부분입니다. 실제로 stagingrelease 로 빌드 시에 flavor 값이 stagingrelease 로 뜨는 걸 확인했습니다. node_modules 에서 react-native-config 폴더를 찾아서 dotenv.gradle 에서 찍으니까 확인이 가능하더라고요. 제가 궁금한 점은 왜 루트에서 .env 파일만을 읽어서 BuildConfig.java 에서 사용하려고 하는걸까요? .env.staging 을 참조하지 않는 이유가 궁금합니다. 아래는 dotenv.gradle 파일입니다. import java.util.regex.Matcher import java.util.regex.Pattern def getCurrentFlavor() { Gradle gradle = getGradle() def pattern = Pattern.compile("(?:.*:)*[a-z]+([A-Z][A-Za-z0-9]+)") def flavor = "" gradle.getStartParameter().getTaskNames().any { name -> Matcher matcher = pattern.matcher(name) if (matcher.find()) { flavor = matcher.group(1).toLowerCase() return true } } println "Current flavor: $flavor" return flavor } def loadDotEnv(flavor = getCurrentFlavor()) { def envFile = project.hasProperty("defaultEnvFile") ? project.defaultEnvFile : ".env" if (System.env['ENVFILE']) { envFile = System.env['ENVFILE'] } else if (System.getProperty('ENVFILE')) { envFile = System.getProperty('ENVFILE') } else if (project.hasProperty("envConfigFiles")) { project.ext.envConfigFiles.any { pair -> if (flavor.startsWith(pair.key.toLowerCase())) { envFile = pair.value return true } } } println "Reading env from: $envFile" def env = [:] File f = new File("$project.rootDir/../$envFile"); if (!f.exists()) { f = new File("$envFile"); } if (f.exists()) { println "Found env file: $f" f.eachLine { line -> def matcher = (line =~ /^\s*(?:export\s+|)([\w\d\.\-_]+)\s*=\s*['"]?(.*?)?['"]?\s*$/) if (matcher.getCount() == 1 && matcher[0].size() == 3) { env.put(matcher[0][1], matcher[0][2].replace('"', '\\"')) // 각 환경 변수 키-값 쌍 출력 println "Loaded env variable: ${matcher[0][1]} = ${matcher[0][2]}" } } } else { println("**************************") println("*** Missing .env file ****") println("**************************") } project.ext.set("env", env) } loadDotEnv() android { defaultConfig { project.env.each { k, v -> def escaped = v.replaceAll("%","\\\\u0025") buildConfigField "String", k, "\"$v\"" resValue "string", k, "\"$escaped\"" println "Set buildConfigField and resValue: $k = $v" } } } 안드로이드 스튜디오 터미널에서 ./gradlew assembleStagingRelease 명령어를 치게 될 경우, Current flavor: stagingrelease Reading env from: .env.staging ************************** *** Missing .env file **** ************************** 이런 로그가 뜨고 있습니다.
안녕하세요. 현재 Websocket과 stompjs v6.0.0을 활용해 채팅을 구현했습니다. roomId로 여러 채팅방을 만들 수 있게 구현했고, 현재 새로고침을 하지 않는 이상 잘 돌아갑니다. 그러나, 새로고침 할 시에는 바로 연결이 끊겨 이전의 채팅 내역도 보이지 않고, 연결, 구독 내역이 사라집니다 ... 어떻게 reconnect 해야할까요? 단순히 채팅 페이지에서 useEffect로 connect를 다시 하니 이미 연결 구독이 된 상태라고 뜨더라구요 .... ㅠㅠ (고민글을 올렸을 때 채팅방이 생성되고, 연결 구독이 됩니다. 채팅 시작 버튼을 눌렀을 경우에는 본인이 연결 구독이 되어 1대 1로 상대방과 채팅이 시작되는 구조입니다. ) import { CompatClient, Stomp } from "@stomp/stompjs"; import { createContext, useContext, useMemo, useRef } from "react"; import { useSetRecoilState } from "recoil"; import { messageState } from "../../states/chatting"; import audio from "../../assets/audios/chatting.mp3"; const ChatContext = createContext( {} as { connect: (roomId: number) => void; disconnect: () => void; send: (roomId: number, message: string) => void; }, ); export const useChatContext = () => useContext(ChatContext); export function ChatProvider({ children }: any) { const setMessages = useSetRecoilState(messageState); const token = localStorage.getItem("accessToken"); // 채팅 연결 구독 const client = useRef<CompatClient>(); const connect = (roomId: number) => { client.current = Stomp.over(() => { const sock = new WebSocket("wss://m-ssaem.com:8080/stomp/chat"); return sock; }); client.current.connect( { token: token, }, () => { client.current && client.current.subscribe( `/sub/chat/room/${roomId}`, (message) => onMessageReceived(message, roomId), { token: token!, }, ); }, ); return client; }; const onMessageReceived = (message: any, roomId: number) => { const audioElement = new Audio(audio); audioElement.play(); setMessages((prevMessages) => { const updatedMessages = { ...prevMessages, [roomId]: [...(prevMessages[roomId] || []), JSON.parse(message.body)], }; return updatedMessages; }); }; // 채팅 나가기 const disconnect = () => { if (client.current) { client.current.disconnect(() => { window.location.reload(); }); } }; // 채팅 보내기 const send = (roomId: number, message: string) => { if (client.current) { client.current.send( `/pub/chat/message`, { token: token, }, JSON.stringify({ roomId: roomId, message: message, type: "TALK", }), ); } }; const handlers = useMemo(() => ({ connect, disconnect, send }), []); return ( <ChatContext.Provider value={handlers}>{children}</ChatContext.Provider> ); } ----------이 부분은 connect 하는 부분입니다 --------- const { connect } = useChatContext(); const chatRoomId = worryBoard && worryBoard.chatRoomId; const handleStartChatting = () => { navigate(`/chatting`); connect(chatRoomId!!); }; ------------ 채팅 페이지는 따로 있습니다 --------------