안녕하세요! 오늘은 React Hook Form 과 useRef 의 조합을 통해 특정 input 요소에 직접 전근하는 방법에 대해 공유하고자 합니다. 최근 게시판 프로젝트에서 게시글을 열 때 댓글 작성자 필드에 자동으로 포커스를 주고 싶었는데 React Hook Form 도입 시에 이를 구현하는데 어려움을 겪었습니다. 이에 대한 내용과 해결방법을 공유합니다.
🤔 문제상황 왜 ref={commentAuthorInputRef} 로는 동작하지 않을까?
React Hook Form 을 사용할 때, 주요 기능 중 하나는 register 함수입니다. 이 함수는 input 필드를 관리하고, 유효성 검사나 폼 제출 등의 다양한 기능을 제공합니다. 제가 처음에 사용한 방식은 아래와 같았습니다.
그런데, 위와 같은 방식으로 구현하면 React Hook Form 의 register 와 ref 와 동시에 연결되지 않아, 입력 값이 undefined 로 반환되는 문제가 발생했습니다.
register 함수는 input 필드를 등록하면서 해당 필드의 참조 ref 를 내부적으로 관리합니다. 그렇기 때문에, 외부에서 별도의 ref 를 연결할 때는 register 와의 연동이 중첩될 수 있습니다. 이 때문에, 내부적으로 폼 값의 상태 관리나 유효성 검사와 같은 기능들이 제대로 동작하지 않게 됩니다.
🌟 해경 방법: ref 를 함수로 정의하기
React Hook Form 의 register 함수는 ref 와 함께 객체를 반환합니다. 이를 입력 요소에 연결하면서 동시에 외부 ref 에도 연결해야 했기 때문에, 함수를 사용하여 이를 해결해야 합니다.
<input
{...commentRegister('author', {
required: '댓글 작성자는 필수 입력입니다.'
})}
ref={(e) => {
commentRegister('author').ref(e); // react-hook-form 을 위한 ref
commentAuthorInputRef.current = e; // 외부 ref
}}
...
/>
위 코드에서 중요한 점은 ref 함수가 두 가지 연결 작업을 동시에 수행한다는 것입니다. 1. React Hook Form 의 ref 에 요소(HTMLInputElement)를 연결 2. 외부 ref에도 동일한 요소(HTMLInputElement)를 연결
이를 통해 React Hook Form 의 기능을 그대로 활용하면서도, 외부에서도 input 요소에 접근할 수 있게되었습니다.🤭
🎬 결론
React Hook Form 은 매우 편리한 도구이지만, 때때로 다른 React 기능과의 조합이 필요할 때가 있습니다. 오늘 소개한 방식은 그러한 경우에도 도움이 될 것입니다. 특히, 외부 ref와 함께 React Hook Form 을 사용하고자 할 때 이 방식이 큰 도움이 될 것입니다.
오늘은리액트에서 동적 라우팅을 사용하여 페이지모드를어떻게활용하는지 작성합니다. 게시판 화면을 만들어 보면서 등록, 수정, 상세의 화면을 단 하나의 페이지에서 관리하는 것이 목표였습니다. 이를 리액트로개발하면서 페이지간의이동및데이터의활용을어떻게해야효과적으로구현할수있을까? 고민하다가 동적 라우팅 활용을 알게 되었습니다.
페이지의모드는 view, add, edit 세가지로 구분됩니다. 위코드처럼 App 에서 활용된느 각 모드 타입을 별도의 파일로 분리하여 관리하면 프로젝트의 구조가 더욱 명확해지고 유지 보수가 쉬워집니다.
🌟 리액트 라우터의 활용
리액트에서 페이지간의 이동을 관리하는 가장 효과적인 방법 중 하나는 리액트 라우터를 활용하는 것입니다. 특히 동적 라우팅은 동일한 UI 구조를 가지면서 다양한 데이터를 보여주는 페이지를 구현할 때 매우 유용합니다. 페이지 모드를 활용해 라우터에서는 다음과 같이 경로와 컴포넌트를 연결해 주었는데요..
리엑트 라우터의 동적 라우팅을 활용하면, 사용자의 요청에 따라 페이지의 내용과 상태를 유연하게 변경할 수 있습니다. 페이지 모드의 관리와 라우팅을 최적화하는 방법을 살펴봤는데, 리엑트 프로젝트에서 이 기법을 적용하면 사용자 경험은 물론, 개발자의 작업 효율도 상당히 증진될 것이라 생각합니다. 🙂
안녕하세요 오늘은 Spring에서 자주 마주치는 문제 중 하나인 API 잘못된 메시지 요청에 대한 이야기를 해보려고 합니다. 그리고 이를 어떻게 처리했는지 작성해 볼게요.
게시판 프로젝트를 만들어 보면서 백엔드에서 클라이언트로부터 API 요청을 받게 되면, 요청의 JSON 포맷에 오류가 있는 경우가 있었습니다. 그 중에서도 알 수 없는 필드를 전달 받았을 때 이 예외가 발생합니다. 이 문제를 어떻게 해결 할 수 있을까 고민을 했고..
🤔 HttpMessageNotReadableException 처리
Spring 에서 제공하는 @ExceptionHandler 를 사용하여 HttpMessageNotReadableException 을 처리하면서, 그 원인이 되는 cause 를 통해 더 구체적인 예외 상황을 파악하는 방법을 알게되었습니다.
@ExceptionHandler(value = {HttpMessageNotReadableException.class})
public ResponseEntity handleJsonParseException(HttpMessageNotReadableException ex) {
final Throwable cause = ex.getCause();
...
}
이 때, 발견한 것이 UnrecognizedPropertyException 이라는 것입니다. 이 예외는 JSON의 알 수 없는 필드 때문에 발생하므로, 해당 필드의 이름을 얻어와 클라이언트에게 더 구체적인 오류 메시지를 전달하고자 했습니다.
if (cause instanceof UnrecognizedPropertyException) {
final UnrecognizedPropertyException unrecognizedPropertyException = (UnrecognizedPropertyException) cause;
final String fieldName = unrecognizedPropertyException.getPropertyName();
...
}
그리고 이렇게 얻어온 필드 이름을 ErrorCode.UNKNOWN_FIELD 와 함께 반환하여 클라이언트에게 해당 필드가 문제라는 것을 응답하였습니다. 이렇게 처리하면, 클라이언트는 어떤 필드 때문에 요청이 실패했는지 쉽게 파악할 수 있습니다.😀
전체 소스를 보면 아래와 같이 @ExceptionHandler 를 구축해보았습니다.
@ExceptionHandler(value = {HttpMessageNotReadableException.class})
public ResponseEntity handleJsonParseException(HttpMessageNotReadableException ex) {
final Throwable cause = ex.getCause();
if (cause instanceof UnrecognizedPropertyException) {
final UnrecognizedPropertyException unrecognizedPropertyException = (UnrecognizedPropertyException) cause;
final String fieldName = unrecognizedPropertyException.getPropertyName();
final String errorMessage = String.format(ErrorCode.UNKNOWN_FIELD.getMessage() + " : '%s'", fieldName);
final ResponseModel responseModel = ResponseModel.failure(ErrorCode.UNKNOWN_FIELD, errorMessage);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseModel);
}
// 기본 메시지 처리
final ResponseModel responseModel = ResponseModel.failure(ErrorCode.INVALID_JSON, ex.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseModel);
}
그 밖에 JSON 문법 오류 / 타입 불일치 / JSON 구조의 불일치 / 데이터 바인딩 오류가 있는 경우는 기본 메시지 처리로 응답하는 INVALID_JSON 으로 응답하도록 구현하였습니다.
🤠 Response 응답 메시지
구현된 핸들러의 응답메시지는 한번 살펴보겠습니다.🙂
잘못된 필드 데이터 전달이 되었을 경우..
UnrecognizedPropertyException 예외
JSON 문법오류 등 기본 메시지 전달이 되었을 경우..
그 밖의 HttpMessageNotReadableException 예외
🥸 결론
예외 처리는 단순히 오류를 포착하는 것을 넘어 중요한 역할을 하는거 같습니다. 이를 통해 사용자와의 원활한 소통과 협업이 가능하며, 이로 인해 보다 나은 서비스를 제공할 수 있게 됩니다. 게시판 프로젝트를 만들어 보면서 이에 대한 연구와 고민을 지속적으로 해 나갈 계획입니다.🙂
여기서 useAppNavigate 라는 custom hook 은 두가지 기능을 제공합니다.
1. navigateBack: 현재 페이지에서 이전 페이지로 돌아가는 기능
2. navigateTo: 원하는 경로로 이동하는 기능
이제 이 custom hook을 어떻게 사용하는지 간략하게 살펴보겠습니다.
🤠 navigateBack 활용
때론 사용자에게 '뒤로가기' 기능을 제공하고 싶을 때가 있습니다. 이때 navigateBack 함수를 사용하면 간단하게 구현할 수 있습니다.
const { navigateBack } = useAppNavigate();
// '뒤로가기' 버튼이 클릭되었을 때 실행
<button onClick={navigateBack}>뒤로 가기</button>
🥸 navigateTo 활용
특정 경로로 이동하고자 할 때 navigateTo 함수를 사용합니다.
const { navigateTo } = useAppNavigate();
// '/profile' 경로로 이동하고자 할 때
<button onClick={() => navigateTo('/profile')}>프로필 보기</button>
요약하면 React Router 의 useNavigate 는 굉장히 유용한 기능을 제공하지만, 특정한 사용 상황에 따라 조금 더 확장된 기능이 필요할 때가 있습니다. 이럴 때 위와 같은 custom hook 을 활용하면 보다 편리하게 라우팅 관리를 할 수 있습니다.
🤔 왜 Custom Hooks 을 만들었는가?
useNavigate 와 같은 기본 Hooks 가 있다면, 왜 추가적으로 custom Hooks 를 만들었는지 궁금할지도 모릅니다. 이에 대해서 간단하게 설명드리겠습니다.
1. 코드 간결화: 반복되는 라우팅 코드를 간단하게 만들 수 있습니다.
2. 명확성: 함수의 이름을 통해 용도를 바로 알 수 있게 됩니다. 예를 들면, navigateBack 은 바로 뒤로 가는 기능임을 알 수 있습니다.
3. 유연성: 나중에 라우팅 관련 로직을 변경할 때, 한 곳에서만 수정하면 되므로 관리가 쉽습니다.
4. 재사용: 여러 컴포넌트에서 같은 로직을 쉽게 재사용할 수 있습니다.
🤭 결론
Custom Hooks 는 프로젝트의 효율성과 가독성을 높이는 핵심 도구입니다. 이를 통해 반복되는 코드를 줄이며, 라우팅과 같은 기능을 더 명확하고 직관적으로 표현할 수 있게 되었습니다. 이는 코드의 관리와 수정을 쉽게 하며, 다른 개발자들이 코드를 이해하고 활용하는 데에도 큰 도움을 줍니다.
안녕하세요! 오늘은 Spring Boot 에서 API 응답과 예외 처리 모델을 구축해봤던 내용을 공유해 보려고 합니다. 코드들도 하나하나 분석해 볼께요. 스타트~!
1. ResponseModel - 응답 모델
먼저, 모든 API 응답에 공통적으로 사용될 ResponseModel 을 살펴봅니다.
@Getter
@Setter
@ToString
@AllArgsConstructor(staticName = "of")
public class ResponseModel<T> {
private boolean success;
private T data;
private ErrorModel error;
public static <T> ResponseModel<T> of(boolean success, T data) {
return ResponseModel.of(success, data, null);
}
public static ResponseModel of(boolean success, ErrorCode code) {
return ResponseModel.of(success, null, ErrorModel.of(code.name(), code.getMessage(), null));
}
public static ResponseModel of(boolean success, ErrorCode code, Exception ex) {
return ResponseModel.of(success, null, code, ex);
}
public static <T> ResponseModel<T> of(boolean success, T data, ErrorCode code, Exception ex) {
final ErrorModel error = (code != null) ? ErrorModel.of(code, ex) : null;
return ResponseModel.of(success, data, error);
}
}
주요 구성 요소:
1. success: 요청 처리의 성공 여부를 나타냅니다.
2. data: 요청에 대한 결과 데이터를 포함합니다.
3. error: 발생한 오류에 대한 정보를 포함하는 ErrorModel 객체 입니다.
ResponseModel 에는 여러 static 생성 메서드가 포함되어 있습니다. 이 메서드들을 통해 성공 또는 오류 상황에 따라 쉽게 응답 객체를 구성할 수 있습니다.
2. ErrorModel - 에러 모델
ErrorModel 은 오류에 대한 정보를 표준화하여 전달하기 위한 모델입니다.
package com.board.backend.model;
import com.board.backend.common.ErrorCode;
import com.board.backend.common.utils.ObjectUtil;
import com.board.backend.common.utils.StringUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor(staticName = "of")
public class ErrorModel {
private String code;
private String message;
private ErrorData data;
@Getter
@Setter
@ToString
@AllArgsConstructor(staticName = "of")
public static class ErrorData {
private String exceptionMessage;
private String stackTrace;
}
public static ErrorModel of(ErrorCode code, Exception ex) {
final String exceptionMessage = ObjectUtil.nonEmpty(ex) ? ex.getMessage() : null;
final String stackTrace = ObjectUtil.nonEmpty(ex) ? getStackTraceAsString(ex) : null;
final ErrorData data = (StringUtil.nonEmpty(exceptionMessage) || StringUtil.nonEmpty(stackTrace)) ? ErrorData.of(exceptionMessage, stackTrace) : null;
return ErrorModel.of(code.name(), code.getMessage(), data);
}
private static String getStackTraceAsString(Exception ex) {
final StringBuilder sb = new StringBuilder();
for (StackTraceElement element : ex.getStackTrace()) {
sb.append(element.toString());
sb.append("\n");
}
return sb.toString();
}
}
주요 구성 요소:
1. code: 오류 코드를 나타냅니다.
2. message: 오류 메시지를 나타냅니다.
3. data: 오류 발생 시의 추가 정보를 나타내는 ErrorData 객체입니다.
ErrorData 클래스는 발생한 예외의 메시지와 스택 트레이스 정보를 포함합니다. 이를 통해 오류의 원인을 더욱 자세히 파악할 수 있습니다.(스택 트레이스란? 프로그램에서 예외가 발생했을 때 해당 예외의 발생 경로를 추적하는 정보입니다. 이는 메서드 호출 순서와 라인 번호를 포함하여 오류가 발생한 위치와 그 원인을 확인할 수 있게 도와줍니다.)
3. ErrorCode - 에러 코드
@Getter
@AllArgsConstructor
public enum ErrorCode {
SERVER_ERROR("서버 에러"),
ARGUMENT_TYPE_MISMATCH("잘못된 파라미터타입이 전달되었습니다."),
BOARD_NOT_FOUND("글이 존재하지 않습니다");
...
private String message;
}
각 오류 유형에 대한 메시지를 함께 제공하여 사용자에게 의미 있는 오류 메시지를 전달 합니다. enum 타입으로 코드와 메시지를 함께 관리하였습니다.
4. ExceptionAdvice - 전역 예외 처리기
Spring Boot 에서 예외 처리의 중요성을 강조하며, 이를 위해 @ControllerAdvice 와 @ExceptionHandler 를 통해 효과적인 전역 예외 처리 방식을 제공합니다.
@ControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(value = {Exception.class})
public ResponseEntity handleException(Exception ex, WebRequest request) {
final ResponseModel responseModel = ResponseModel.of(false, ErrorCode.SERVER_ERROR, ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(responseModel);
}
@ExceptionHandler(value = {MethodArgumentTypeMismatchException.class})
public ResponseEntity handleTypeException(Exception ex) {
final ResponseModel<Object> responseModel = ResponseModel.of(false, ErrorCode.ARGUMENT_TYPE_MISMATCH, ex);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseModel);
}
...
}
1) @ControllerAdvice 의 역할
@ControllerAdvice 는 Spring 에서 모든 컨트롤러에서 발생할 수 있는 예외들을 한곳에서 관리하고 처리하기 위해 도입된 어노테이션입니다. 이를 통해 코드의 중복을 줄이고, 일관된 예외 처리 로직을 구성할 수 도와줍니다.
2) @ExceptionHandler
@ExceptionHandler 는 특정 예외를 처리할 메서드를 지정하기 위한 Spring MVC 의 어노테이션입니다. 그 자체로도 유용하지만, @ControllerAdvice와 결합하여 사용할 경우, 애플리케이션 전체의 예외를 중앙에서 효율적으로 관리할 수 있게 됩니다.
예시:
// 단일 지정
@ExceptionHandler({CustomException.class})
// 다중 지정
@ExceptionHandler({CustomException1.class, CustomException2.class, CustomException3.class})
이렇게 설정하면, 위에서 지정된 예외들이 발생했을 때 동일한 방식으로 처리됩니다.
3) 확장성
@ControllerAdvice 의 장점중 하나는 그 확장성입니다. 새로운 예외를 추가하거나 예외 처리 방식을 수정해야 할 때, @ExceptionHandler 를 적절한 메서드에 추가하거나 수정함으로써 중앙에서 예외 처리 로직을 쉽게 관리하고 확장할 수 있습니다.
🤠 결론
Spring Boot 환경에서 나름대로 생각해서 구현했던 응답과 예외 처리 방법에 대해 알아보았습니다. 일관된 응답과 정확한 예외 처리는 API의 신뢰성을 높이는 핵심 요소로, 이를 통해 사용자와의 신뢰 관계를 구축하고, 개발팀 간의 협업 효율을 높일 수 있습니다. 🙂
JSON 내의 null 값을 제거하는 것은 간단한 절차처럼 보일 수 있지만, 이러한 작은 최적화가 서비스의 응답 시간 개선과 클라이언트의 처리 부담 감소에 큰 도움을 줍니다. 특히, 대용량 데이터를 다루는 서비스나 리소스가 제한된 모바일 환경에서는 이런 최적화가 더욱 중요합니다.
오늘은 MyBatis XML Mapper에서 Java의내부클래스참조시발생하는문제와그에대한해결방법을알려드리려합니다. 개인적으로이문제로인해상당한시간을헤맸기에이경험을여러분과공유하고싶습니다.
🥲 문제 상황
먼저, 아래는 Java 내부클래스를포함한 BoardDto 모델입니다.
package com.board.backend.model;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BoardDto {
@Getter
@Setter
public static class Response {
private long id;
private String title;
private String content;
private String writer;
private long view_count;
private Long created_id;
private LocalDateTime created_dt;
private LocalDateTime modified_dt;
}
}
이모델을 MyBatis XML Mapper에 resultType으로설정하려고했을때, 내부클래스 Response를참조해야했습니다. 처음작성한코드는아래와같습니다.
<select id="findById" resultType="com.board.backend.model.BoardDto.Response">
SELECT *
FROM board
WHERE id = #{id}
</select>
하지만위와같이설정했을때, 연결이제대로이루어지지않았고, 다음과같은에러가발생했습니다.
Error creating bean with name 'boardMapper' defined in file ...
Cannot resolve reference to bean 'sqlSessionTemplate' while setting bean property 'sqlSessionTemplate'
🤔 원인
sqlSessionTemplate 연결 에러의 원인은 다양합니다: 설정 파일 누락, 설정 파일 위치 불일치, 의존성 문제, 데이터 소스 연결 문제, 빈 이름 불일치, 생성자/세터 주입 문제 등. 이 모든 가능성을 확인했지만, 문제의 원인을 찾지 못하고 있었습니다.
그러던중, Java에서내부클래스를참조할때 $ 기호로연결된다는것을알게되었습니다.
🙂 해결방법
Mapper XML에서 resultType을올바르게설정하기위해선 $ 기호를사용해야합니다.
<select id="findById" resultType="com.board.backend.model.BoardDto$Response">
SELECT *
FROM board
WHERE id = #{id}
</select>
핵심은 BoardDto.Response 대신 BoardDto$Response를사용하는것입니다.
최근 몇 년간 전 세계 개발자는 자바스크립트에 뜨럽게 열광하고 있습니다. 자바스크립트는 현재 웹 애플리케이션에서 가장 핵심적인 역할을 하고 있습니다. 자바스크립트 언어만으로 데스크톱 애플리케이션, 모바일 애플리케이션 등 규모가 큰 애플리케이션을 만들 수 있는 시대입니다. 대규모 애플리케이션 중 프런트엔드 사이드에서 돌아가는 애플리케이션 구조를 관리하려면.. 어떻게 해야 할까요?
이에 페이스북 개발팀은 아이디어를 고안해 냈습니다.
어떤 데이터가 변할 때마다 어떤 변화를 줄지 고민하는 것이 아니라 그냥 기존 뷰를 날려 버리고 처음부터 새로 렌더링 한다.
이렇게 하게되면 애플리케이션 구조가 매우 간단해지고, 작성해야 할 코드양이 많이 줄어 들며, 더 이상 어떻게 변화를 줄지 신경 쓸 필요가 없고, 그저 뷰가 어떻게 생길지 선언만 하면 되며, 데이터 변화가 있으면 기존에 있던 것을 버리고 새로 렌더링 되게 됩니다.
페이스북 개발 팀은 이런 방식을 채택하여 최대한 성능을 아끼고 편안한 사용자 경험(user experience)을 제공하면서 구현하고자 개발한 것이 리액트(React) 입니다.
리액트의 이해
이미지출처: reactjs
리액트는 자바스크립트 라이브러리로서 사용자 인터페이스를 만드는 데 사용합니다. 관리 구조가 MVC, MVW 등인 프레임워크와 달리 오직 V(View)만 신경 쓰는 라이브러리 입니다.
리액트에서는 특정 부분이 어떻게 생길지 정하는 선언체가 있는데 이를 컴포넌트(component) 라고 합니다. 컴포넌트는 재사용이 가능한 API 로 수많은 기능들을 내장하고 있으며 컴포넌트 하나에서 컴포넌트의 생김새와 작동 방식을 정의하여 사용하게 됩니다.
또한 사용자 화면에 뷰를 보여 주는 것을 렌더링이라고 합니다.
리액트 라이브러리는 뷰를 어떻게 렌더링하길래 데이터가 변할 때마다 새롭게 리렌더링 하면서 성능을 아끼고, 최적의 사용자 경험을 제공 할 수 있을까요? 이를 이해하려면 '초기렌더링'과 '리렌더링' 개념을 이해해야 합니다.
1) 초기 렌더링
컴포넌트의 render 함수를 통하여 렌더링이 이루어지게 됩니다. 렌더함수는 뷰가 어떻게 생겼고 어떻게 작동하는지에 대한 정보를 지닌 객체를 반환합니다. render 함수가 실행하면 렌더링이 이루어지고 HTML 마크업(markup) 을 만들고 이를 DOM 요소 안에 주입하는 작업이 이루어 지게 됩니다.
2) 리렌더링(조화 과정)
뷰를 업데이트 할적에 "업데이트 과정을 거친다" 라고 하기보다 "조화 과정을 거친다" 라고 하는 것이 더 정확한 표현입니다. 이유는 새로운 DOM 요소로 갈아 끼우기 때문입니다.
리액트는 데이터가 변경되면 render 함수가 반환하는 결과를 곧바로 DOM 에 반영하지 않고, 이전에 render 함수가 만들었던 컴포넌트 정보와 현재 render 함수가 만든 컴포넌트 정보를 비교한후 DOM 트리를 업데이트 하게 됩니다.
리액트의 특징
리액트의 주요 특징 중 하나는 Virtual DOM을 사용하는 것
우선 DOM 이 무엇인지 부터 알아보면.. DOM 이란 문서객체 모델(Document Object Model)의 약어입니다. 웹 브라우저는 DOM 을 활용하여 객체에 자바스크립트와 CSS 를 적용할 수 있습니다. DOM 은 트리형태로 구성되어 있는데 DOM 을 통해서 특정 노드를 찾거나 수정하거나 제거하거나 원하는 곳에 삽입할 수 있다.
이미지출처: 위키백과
DOM 은 과연 느릴까?
DOM 에는 치명적인 한 가지 문제점이 있습니다. 바로 동적 UI에 최적화되어 있지 않다는 것입니다.
"요즈음 자바스크립트 엔진은 매우 빠른 반면, DOM 은 느리다?" 라고 하는데 이는 정확한 말이 아닙니다.
DOM 자체는 빠릅니다. 단 웹 브라우저 단에서 DOM 에 변화가 일어나면 웹브라우저가 CSS를 다시 연산하고, 레이아웃을 구성하고, 페이지를 리페인트하게되는데 이 과정에서 시간이 허비되는 것입니다. 이에 대한 해결법으로 DOM을 최소한으로 조작하여 작업을 처리하는 방식으로 개선할 수 있습니다.
Virtual DOM
이미지출처: speakerdeck
리액트는 Virtual DOM 방식을 사용하여 DOM 업데이트를 추상화함으로써 DOM 처리 횟수를 최소화하고 효율적으로 업데이트를 진행할 수 있습니다. Virtual DOM 을 사용하면 실제 DOM에 접근하여 조작하는 대신, 이를 추상화한 자바스크립트 객체를 구성하여 사용하게 됩니다. 이는 마치 실제 DOM의 가벼운 사본과 비슷합니다..
리액트에서 실제 DOM을 업데이트 할 때 세가지 절차를 밟게 되는데 살펴보면..
1. 데이터를 업데이트하면 전체 UI를 Virtual DOM에 리렌더링 한다.
2. 이전 Virtual DOM에 있던 내용과 현재 내용을 비교한다.
3. 바뀐 부분만 실제 DOM에 적용한다.
이처럼 리액트는 Virtual DOM 을 통해서 동적 UI 최적화 문제를가지고 있는 DOM 업데이트 방식을 보안하기 때문에 효율적으로 작업을 진행 할 수 있습니다.
또한 일부 웹 프레임워크가 MVC, MVW 등의 구조를 지향하는 것과 달리 리애트는 오직 뷰만 담당하는 라이브러리 로서 취향대로 스택을 설정할 수 있다는 장점을 가지고 있지만 여러 라이브러리를 접해야 한다는 단점을 가지고 있기도 합니다.
영화 백투더퓨처는 마틴박사가 발명한 타임머신 이라고 불리는 자동차를 타고 과거의 시간여행을 할 수 있습니다.
이미지출처: 자유아시아방송
아직까지 현실세계에서는 과거로 시간여행은 불가능하지만.. Git 에서는 과거로 돌아갈 수 있고 과거의 일어난 일을 되돌릴 수 있는 방법을 제공합니다.
과거의 일어난 일을 되돌리는 방법이 두가지가 있습니다. 바로 Reset 과 Revert 명령어를 사용하는 것입니다. Reset 은 시간을 예전으로 되돌리는 것이고 Revert 는 특정 커밋을 없었던 일로 만듭니다.
1. Reset
앞에서 설명한대로 Reset 은 시간을 다시 맞추는 것입니다. 돌아갈려는 커밋 시점으로 레파지토리는 재설정되고 해당 커밋 이후의 이력은 사라집니다. 아래와 같이 사용합니다.
$ git reset <옵션> <돌아가고싶은 커밋 ID>
# 옵션을 적지 않으면 mixed 로 동작합니다.
옵션은 3가지(hard, mixed, soft)가 있습니다.
1) hard: 돌아가려는 이력 이후의 모든 변경 이력을 삭제 합니다.
2) mixed: 이력을 되돌리고 이후에 변경된 내용에 대해서는 남아 있지만 인덱스는 초기화 됩니다. 커밋을 하려면 add 명령어로 stage 에 반영하고 commit 해야 합니다.
3) soft: 돌아가려는 이력으로 되돌아 갔지만, 변경내용은 stage 에 반영되어 있는 상태입니다. 바로 다시 커밋 할 수 있는 상태로 남아 있는 것입니다.
위 명령어를 사용한다고해서 origin 은 변경되어 있지 않습니다. --force 옵션을 주어 이럴땐 강제로 반영해야 하는데.. 다른사람들과 레파지토리르 공유하고 있다면 무조건 하면 안되는 행동입니다.
$ git push -f origin <브랜치명>
2. Revert
Rever 는 특정 커밋을 없었던 일로 만드는 것입니다. Reset 과 다르게 커밋을 삭제하는 것이 아닌 커밋을 추가하는 방식입니다. 이전 이력은 그대로 있고, 되돌리려는 커밋만 되돌리는 방식입니다. 이전의 일은 기억하고 있지만 그 내용은 알지 못하는 것처럼 말이죠. 아래와 같이 사용합니다.
프로그래머라면 누구나 더 나은 개발자가 되고 싶어 합니다. 저도 어떻게 해야 좋은 개발자로 성장할 수 있는 건지 늘 궁금하였습니다. 이 글은 제가 프로그래밍을 처음 시작할 적에 제게 도움을 많이 주었던 멘토가 알려주었던 블로그의 글입니다. 더 나은 개발자로 성장하는 방법을 소개하는데요. 여러분들에게도 좋은 정보를 공유하고 싶어 블로깅하였습니다.
이 글은 NewRelic 블로그의 "8 essential tips to become a better coder"라는 글의 한글 번역입니다. 오역이 있을 수 있습니다.
당신의 프로그래밍 스킬을 향상시키는 방법에 대해 진지하게 얘기해 봅시다. 시작! “쩌는 프로그래머 되기”는 커리어 향상 목표로 삼기에 좋아 보이지만 사실 간단한 일은 아닙니다. 예를 들어 “더 잘하고 싶다” 는 말에는 “더 잘”하는게 어떤 건지 알고 있어야 한다는 가정이 필요하죠. 사실 많은 사람들이 더 나아지기를 원하지만 어떻게 해야하는지에 대해서는 전혀 모르는 경우가 많습니다. 그래서 제가 프로그래밍 스킬을 향상시킬 수 있는 8가지 실행 가능한가이드라인을 알려드릴게요. 순서대로 따라하시면 됩니다. 이 지혜의 조각들은 컴퓨터 산업의 지난 35년 간 축적되었고 이를 밝히고 기록한 사람들은 이를 발에 있는 메뚜기로 사용해 왔습니다.
1. 배울 것이 얼마나 많은지 상기하세요.
무언가를 배우는 첫 걸음은 그것을 모른다는 것을 깨닫는 것입니다. 어찌보면 당연한 얘기지만, 경험 많은 프로그래머들은 스스로 알고 있다는 착각을 극복하는데 얼마나 많은 시간이 필요했는지 알고 있습니다. 많은 전산과 학생들이 “나는 존잘” 이라는 허세, 모든 것을 알고 있다는 강한 확신과 그것을 직장에 가서 동료들에게 증명해보이고픈 강한 욕망을 가지고 학부를 졸업합니다. 다시 말해 “나는 내가 뭘 하고 있는지 알아!” 라는 태도는 새로운 것을 배우는 길에서 멀어지게 합니다.
2. 당신이 맞다는걸 증명하려고 하지 마세요
(단지 잘 하는게 아니라) 훌륭해지기 위해서는 반드시 경험에서 배워야 합니다. 하지만 경험은 우리에게 잘못된 행동을 반복하게 하고 나쁜 습관을 만들어 줄 수도 있으니 조심해야 합니다. 아마 8년 경력의 프로그래머… 8년 간 똑같은 경험만 8번 반복한 프로그래머를 본 적이 있을겁니다. 그렇게 되지 않기 위해서는 당신이 하는 모든 일을 다시 돌아보고 스스로에게 질문하세요. “어떻게 하면 이걸 더 잘 할 수 있지?” 초짜 개발자들(그리고 너무 경험이 많은 개발자들)은 자기 코드를 보면서 그 놀라움에 스스로 감탄합니다. 그들은 자기 코드가 잘 돈다는것을 증명하기 위해서 테스트를 작성합니다. 코드가 잘 못 도는 경우를 찾는게 아니라요. 진정으로 훌륭한 프로그래머는 적극적으로 어디가 잘 못 됐는지를 찾습니다. 자기가 놓친 결함은 결국 사용자가 발견하게 되리라는 것을 알고 있기 때문이죠.
3. “동작하는 코드” 는 끝이 아니라 시작입니다.
네, 첫 걸음은 항상 스펙에 따라 동작하는 소프트웨어를 작성하는 것이죠. 평균적인 프로그래머들은 여기서 끝내고 다음으로 이동합니다. 하지만 일단 “끝”났다고 거기서 멈추는 것은, 스냅 사진을 한 장 찍고 그것이 예술 작품이 되기를 기대하는 것과 같습니다. 훌륭한 프로그래머들은 첫 반복은 그저 첫 반복일 뿐이라는 것을 알고 있습니다. 당신이 작성한 소프트웨어는 동작하지만 — 축하드립니다! — 끝난 것은 아닙니다. 자 이제, 더 낫게 만드세요. 이 과정의 일부는 무엇이 “더 나은” 것인지를 정의하는 것입니다. 더 빠르게 하는 것이 가치있는지? 더 문서화하기 쉽게? 더 재사용하기 편하게? 더 신뢰성있게? 대답은 각 어플리케이션에 따라 달라지지만, 이 과정 자체는 동일합니다.
4. 세 번 다시 작성하세요.
좋은 프로그래머들은 동작하는 소프트웨어를 작성합니다. 훌륭한 프로그래머들은 엄청나게 잘 동작하는 소프트웨어를 작성합니다. 첫 시도에서 그렇게 만들기는 매우 어렵습니다. 최고의 소프트웨어들은 보통 세 번 다시 작성됩니다.
우선, 스스로(혹은 고객)에게 그 해결책이 가능한 것임을 증명하기 위한 소프트웨어를 작성하세요. 다른 사람들은 그게 개념 증명인지 눈치채지 못 할 지도 모르지만 당신을 그렇게 생각하고 만듭니다.
두 번째, 동작하게 만드세요.
세 번째, 올바르게 만드세요.
최고의 개발자들의 작업을 보면 이런 단계로 일하는 것에 확신이 서지 않을 수도 있습니다. 그들이 하는 모든게 끝내주는 것 처럼 보이지만, 사실 록스타 개발자들 조차 그들의 소프트웨어를 다른 사람들에게 보여주기 전에 보통 저 첫 번째, 두 번째 버전을 이미 만들었고 내다 버렸을 겁니다. 만든 코드를 버리고 다시 짜는 것은 “더 낫게 만들기”를 당신의 작업 흐름에 녹여넣을 수 있는 강력한 방법이 될 수 있습니다. 다른 건 제쳐두고라도 “세 번 다시 작성하기”는 최소한 당신의 문제에 얼마나 많은 접근법이 있는지를 가르쳐 줄 수 있습니다. 관습적인 방법에만 얽매이는 걸 막아주기도 하고요.
5. 코드를 읽으세요. 많이 읽으세요.
아마 이 조언은 나올 거라고 생각했을 겁니다. 사실 프로그래밍 스킬을 향상시키는 가장 일반적이면서도 가장 값진 방법입니다. 다른 사람의 코드를 읽는 것이 왜 그렇게 중요한지, 그 이유를 말하는 건 조금 덜 뻔할지도 모르겠네요. 다른 사람의 코드를 읽으면 다른 사람들이 프로그래밍 문제를 어떻게 푸는 지를 볼 수 있습니다. 그걸 단지 문자 그대로 보지만 말고 하나의 수업이나 도전 과제로 생각해보세요. 더 잘하기 위해서 스스로 이렇게 질문해 보세요:
나라면 저 코드 블록을 어떻게 짰을까? 어떻게 다르게 할 수 있을까? 다른 해결책이 있나?
뭘 배웠나? 저 기술을 내가 짰던 코드에 어떻게 적용시킬 수 있을까?(“나는 저기 recursive descent를 쓰는 건 생각도 못해봤어…”)
이 코드를 어떻게 하면 개선할 수 있을까? 만약에 확실히 개선할 수 있고 그게 오픈 소스 프로젝트라면? PR을 보내자!
원 작성자의 스타일로 코드 작성해보자. 이 연습은 그 코드를 만든 사람의 머리 속으로 들어가는 연습이 되고, 당신의 공감 능력을 향상시키는데 도움이 됩니다.
이 단계들을 그저 생각만 하지 말고 직접 대답을 적어보세요. 개인 저널이든, 블로그든, 코드 리뷰 프로세스에서든, 아니면 다른 개발자들과의 커뮤니티에서든 다 좋습니다. 그냥 도움을 줄 수 있는 친구에게 문제를 설명하는 것처럼 당신의 생각을 적고 공유하는 것은 왜 당신이 어떤 다른 사람의 코드를 보고그런 생각을 했는지 이해하는데 도움을 줄 수 있습니다. 이모든 것은 당신의 강점과 약점을 냉정하게 판단— 아까 언급한 자기 반성 — 할 수 있게 해줍니다. 경고: 단지 코드를 많이 읽는다고 해서 훌륭한 프로그래머가 되지는 않습니다. 위대한 문학 작품을 많이 읽기만 한다고 작가가 되는 건 아니죠. 수 많은 개발자가 답을 찾기 위해서 오픈 소스나 다른 소프트웨어들을 보지만 대개 비슷한 문제를 해결하는 것 처럼 보이는 코드를 복사해서 붙여넣기만 합니다. 이렇게 하는 것은 다른 사람의 지혜를 생각 없이 맹목적으로 받아들이는 것이기 때문에 사실상 당신을 더 나쁜 프로그래머로 만듭니다.
6. 코드를 작성하세요. 숙제처럼 하지 말고요.
개인적인 프로그래밍 프로젝트를 진행하는 것에는 많은 장점이 있습니다. 우선 당신의 현재 직장에서는 사용하지 못하지만 당신의 다음 직장을 위한 시장 가치를 여줄 수 있는 도구나 기술을 배울 수도 있죠. 오픈 소스 프로젝트에 기여를 하든 지역 사회 단체의 공익 사업 작업을 맡든 당신은 새로운 기술과 자신감을 얻을 수 있을 겁니다. (개인 프로젝트는 미래의 고용주에게 당신이 배움을 멈추지 않는 사람이라는 증거가 되어줄 수도 있습니다.) 취미로 코드를 작성하는 것의 또 다른 장점은 당신 혼자서 해내야만 한다는 점입니다. 어려운 일이라고 해서 다른 사람에게 넘길 수 없으니 너무 일찍 도움을 요청해버리거나 하는 일은 없습니다. 프로 팁: 개인 프로젝트를 할 때 실패하지 않을 것만 하지는 마세요. 오히려 실패할 필요가 있습니다! 회사 일에서는 실패하고 싶지 않겠지만요.
7. 어떤 방법으로든 다른 개발자와 일대일로 일해보세요.
다른 사람의 말을 듣는 것은 도움이 됩니다. 짝프로그래밍일 수도 있고, 해커톤에 참가할 수도 있고, 아니면 프로그래밍 사용자 그룹에 들어가는 것일 수도 있습니다. 오픈 소스 프로젝트에 기여 중이라면 다른 개발자나 사용자들의 피드백에 주의를 기울여 보세요. 그 사람들의 비판에 어떤 공통점이 있나요? 운이 충분히 좋다면 코딩 기술에서부터 커리어 결정까지 모든 부분에서 당신이 믿고 따를 수 있는 개인적인 멘토를 만나게 될지도 모릅니다. 이런 기회가 있다면 놓치지 마시길.
8. 도구가 아니라 기법을 배우세요.
프로그래밍 언어, 도구, 방법론 등은 흥했다가도 없어집니다. 그래서 가능한 한 많은 경험을 가능한 한 다양한 언어와 프레임워크에서 쌓는게 좋습니다. 프로그래밍의 근본에 집중하세요. 기본은 절대 변하지 않습니다. 프로그래밍하는 것 보다 아키텍쳐에 더 주의를 기울이세요. 만약 어떤 일을 하는데 오직 하나의 올바른 방법만 있다는 확신이든다면 아마도 현실성 검사가 필요한 때 일겁니다. 도그마는 당신이 새로운 것을 배우고 변화를 받아들이는데 족쇄가 될 수 있습니다. 좀 더 얘기할 수도 있습니다만, 자기 향상의 핵심 교리는 멈춰야 할 때를 아는 것이랍니다.