no image
리액트 Ref, state와의 차이
const [fooBar, setFooBar] = useState() Ref는 Reference의 약자로 리액트에서 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공한다. 요소를 getElementById('root')로 DOM에 접근하는 것과 비슷한 역할을 한다. import { useRef } from 'react'; const ref = useRef(0); userRef 훅을 통해서 컴포넌트에 ref를 추가할 수 있다. 그리고 useRef는 다음과 같은 객체를 반환한다. { current: 0 // The value you passed to useRef } current속성에 DOM 요소를 자바스크립트 객체로 받아온다. current 속성을 통해서 ref의 현재 값에 액세스 할 수 있고, 읽..
2024.01.27
no image
Mapping을 인식하지 못할 때(Java VScode Clean)
GetMapping을 해줬지만, postman에서 요청을 보내도 계속 404가 반환됐다. 다른 파일에 테스트 Mapping을 추가해도 마찬가지였다. 오류 찾는 데 상당히 많은 시간을 썼지만, 결론적으로 워크스페이스 오류, 파일을 제대로 인식하지 못했던 것이다. 억울하다. clean cmd + shift + p 혹은 vscode 위 검색창에 > 를 치고 명령어를 입력한다 실행하면 워크스페이스가 정리되고 재실행 된다. 이후 api요청도 성공적으로 반환. 진짜 억울하다.
2024.01.17
no image
[블로그 프로젝트] Response Entity
ResponseEntity 공식문서에 따르면 public class ResponseEntity extends HttpEntity Extension of HttpEntity that adds an HttpStatusCode status code. Used in RestTemplate as well as in @Controller methods. HttpEntity를 상속받아서 HttpStatusCode를 추가하여 HttpRequest에 대한 응답을 포함한 클래스이다. private ResponseEntity(@Nullable T body, @Nullable MultiValueMap headers, Object status) { super(body, headers); Assert.notNull(status,..
2024.01.15
no image
[블로그 프로젝트] DTO 정의
서버에서 ResponseEntity를 통해 오는 응답을 받을 DTO를 프론트에서 정의해준다. 타입스크립트의 장점을 살려 타입을 맞춰준다. 서버에서 오는 ResponseDto enum을 통해 code를 매크로로 정의한다 (VO) Code는 ResponseCode, message는 string 타입으로 형을 맞춰준다. 서버에서 json객체로 오는 response는 구조화 대입을 통해 RespoonseDto에 할당된다.
2024.01.14
no image
스프링 부트 Controller, DTO
Servlet은 간단히 말하자면 CGI를 개선해서 동적 페이지를 생성해주기 위한 서버 프로그램, 자바 클래스이다. 추후에 다룰 예정이다. 클라이언트에서 요청은 Dispatcher Servlet이 먼저 처리한다. 프론트 컨트롤러 라고도 한다. Dispatcher Servlet은 요청에 매핑된 적절한 Controller를 찾아 요청을 위임한다. 과거엔 Servlet이 web.xml에서 맵핑된 URL을 찾아주어야 했지만, Dispatcher Servlet을 통해 Controller만 적절히 구현해두면 알아서 적절하게 요청을 위임해주게 되었다. // Servlet alias HelloServlet test.servlet.HelloServlet // URL에 필요한 Servlet을 맵핑 시켜준다 // /hello..
2024.01.12
no image
[블로그 프로젝트] 리액트(타입스크립트) 프로젝트 생성, index.html, App.tsx, index.tsx
npx create-react-app projectName --template typescript 타입스크립트를 사용하는 리액트 프로젝트를 생성 package.json 과 함께 기본 디렉토리 구조가 생선된다. public경로 안데 index.html 파일을 볼 수 있다. 큰 내용 없는 html 파일이다. 중요한건 "root" id 를 달고 있는 div 태그이다. 이후에 살펴보겠지만 해당 요소에 리엑트 엘리먼트가 표시된다. 프로젝트 진행을 위한 디렉토리 구조이다. index.tsx를 살펴보자 import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import..
2024.01.12
no image
[블로그 프로젝트] 스프링 프로젝트 생성
Spring initializr를 vs code 에서 명령어로 실행가능 하다. 이후에 group id, artifact name 등을 설정해주면 된다. 패키지 dependencies를 설정해준다. 내가 고른 패키지의 간단한 설명 Spring Web(Spring MVC) - web 프로젝트에 필요한 기능 제공, mvc패턴과 request mapping 등 Validation - 어노테이션을 통해 유효성을 검증할 수 있다. (requestBody 등) Spring Security -보안 관련 메소드 제공 Spring Data JPA - Repository 인터페이스를 를 통해 데이터 베이스 CRUD작업 (JPA기반을 더 편하게) MySQL Driver - 이름 그대로 Mysql 데이터베이스 드라이버 Lomb..
2024.01.12
no image
[블로그 프로젝트] ERD EDITOR
vscode 확장프로그램으로 다운받을 수 있다. 데이터베이스 ERD를 GUI로 직관적으로 생성 및 관리할 수 있다. vuerd.json 형식의 파일을 생성해준 뒤 ERD Editor를 통해 열어준다 마우스로 편하게 ERD를 그리고 key constraints도 추가할 수 있다. 그리고 가장 강력한 기능이라고 할 수 있는 DDL 생성. 상단바에 SQL DDL을 클릭한다. 내가 그린 ERD에 맞춰서 SQL을 작성해준다.
2024.01.11
const [fooBar, setFooBar] = useState()

Ref는 Reference의 약자로 리액트에서 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공한다.

<div id='root'> 요소를 getElementById('root')로 DOM에 접근하는 것과 비슷한 역할을 한다.

 

 

import { useRef } from 'react';

const ref = useRef(0);

userRef 훅을 통해서 컴포넌트에 ref를 추가할 수 있다.

그리고 useRef는 다음과 같은 객체를 반환한다.

{ 
  current: 0 // The value you passed to useRef
}

 

current속성에 DOM 요소를 자바스크립트 객체로 받아온다.

current 속성을 통해서 ref의 현재 값에 액세스 할 수 있고, 읽기와 쓰기 모두 가능하다.

ref.current = ref.current + 1 같은 식으로 사용할 수 있는 것이다.

이 부분이 state 와의 차이점이다.

const [fooBar, setFooBar] = useState()

위와 같이 set함수를 통해서 fooBar를 초기화시켜야 한다.

state의 사용 목적을 생각해보면 알 수 있다. state는 변화가 있을 때 랜더링을 해야하는 정보에 사용한다. 리액트는 state의 변화를 내부 값의 변화가 아닌 주소값이 이전과 달라졌을 때 감지한다. 때문에 set함수를 통해 초기화 하는 것이다.

그렇다면, 이 차이를 통해서 ref의 목적을 예상해볼 수 있겠다. ref는 변화가 발생하지만 새로운 랜더링을 하고싶지 않을 때 사용할 수 있다.

또한 state는 라이프사이클을 가지는 변수에 가깝고 ref는 위에서 말했듯 dom에 접근하기 위해 사용한다.

공식문서에서는 특정 정보를  '기억'하고싶지만 새로운 랜더링을 촉발하지 않도록 할 때 ref의 사용을 권한다.

 

import React, { useRef, KeyboardEvent } from 'react';

    const App = () => {
      const passwordRef = useRef<HTMLInputElement>(null);

      const onKeyDownHandler = (event: KeyboardEvent<HTMLInputElement>) => {
        if (event.key === 'Enter') {
          passwordRef.current?.focus();
        }
      };

      return (
        <div>
          <input placeholder="아이디 입력" onKeyDown={onKeyDownHandler} />
          <input ref={passwordRef} placeholder="비밀번호 입력" />
        </div>
      );
    };

export default App;

 

많이 사용하는, 로그인 창에서 엔터키를 눌렀을 때 비밀번호 입력 요소로 마우스 포커스가 이동하는 예시 코드이다.

다음 글에서 로그인창, 검색창 등에 사용할 Input 컴포넌트를 생성하면서 ref와 forwardRef를 사용해서 만들어보자.

 

GetMapping을 해줬지만, postman에서 요청을 보내도 계속 404가 반환됐다.

다른 파일에 테스트 Mapping을 추가해도 마찬가지였다.

오류 찾는 데 상당히 많은 시간을 썼지만, 결론적으로 워크스페이스 오류, 파일을 제대로 인식하지 못했던 것이다. 

억울하다.

 

clean

cmd + shift + p 혹은

vscode 위 검색창에 > 를 치고 명령어를 입력한다

 

실행하면 워크스페이스가 정리되고 재실행 된다.

이후 api요청도 성공적으로 반환.

진짜 억울하다.

 

ResponseEntity

공식문서에 따르면


public class ResponseEntity<T> extends HttpEntity<T>

Extension of HttpEntity that adds an HttpStatusCode status code. Used in RestTemplate as well as in @Controller methods.


 

HttpEntity를 상속받아서 HttpStatusCode를 추가하여 HttpRequest에 대한 응답을 포함한 클래스이다.

private ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, Object status) {
		super(body, headers);
		Assert.notNull(status, "HttpStatus must not be null");
		this.status = status;
}

 

생성자를 보면 body와 headers는 부모 클래스인 HttpEntity의 생성자를 호출하고 있고

ResponseEntity에서 추가된 HttpStatusCode는 멤버 변수를 초기화 해주는 것을 볼 수 있다.

 

Response를 위해 정의한 ResponseDto코드

@Getter
@AllArgsConstructor
public class ResponseDto {
	
	private String code;
	private String message;

	public static ResponseEntity<ResponseDto> databaseError() {
		ResponseDto responseBody = new ResponseDto(ResponseCode.DATABASE_ERROR, ResponseMessage.DATABASE_ERROR);
		return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(responseBody);
	}


	public static ResponseEntity<ResponseDto> validationFailed() {
			ResponseDto responseBody = new ResponseDto(ResponseCode.VALIDATION_FAILED, ResponseMessage.VALIDATION_FAILED);
		return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseBody);
	}
}

 

code와 message를 멤버 변수로 가지고 있다.

각자 에러코드와 에러 메세지를 담당한다.

response에 대한 기본 구조(뼈대)라고 볼 수 있다. api요청에 대한 반환값은 모두 ResponseDto를 상속받아서 구현 할것이다.

SignUpResponseDto(예시)

@Getter
public class SignUpResponseDto extends ResponseDto{
	
	private SignUpResponseDto(){
		super(ResponseCode.SUCCESS, ResponseMessage.SUCCESS);
	}

	public static ResponseEntity<SignUpResponseDto> success(){
		SignUpResponseDto result = new SignUpResponseDto();
		return ResponseEntity.status(HttpStatus.OK).body(result);
	}

	public static ResponseEntity<ResponseDto> duplicateEmail(){
		ResponseDto result = new ResponseDto(ResponseCode.DUPLICATE_EMAIL, ResponseMessage.DUPLICATE_EMAIL);
		return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result);
	}

	public static ResponseEntity<ResponseDto> duplicateNickname(){
		ResponseDto result = new ResponseDto(ResponseCode.DUPLICATE_NICKNAME, ResponseMessage.DUPLICATE_NICKNAME);
		return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result);
	}

	public static ResponseEntity<ResponseDto> duplicateTelNumber(){
		ResponseDto result = new ResponseDto(ResponseCode.DUPLICATE_TEL_NUMBER, ResponseMessage.DUPLICATE_TEL_NUMBER);
		return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result);
	}
}

회원가입 요청에 대한 응답 클래스이다.

생성자는 부모 클래스 ResponseEntity의 생성자를 호출해 code와 message를 success로 초기화 한다.

성공시에는 그대로 SignUpResponseDto를 body에 담아 보낸다.

에러가 나면 에러 케이스에 맞게 ResponseDto를 초기화해주고 ResponseDto 그대로 body로 보내준다.

 

postman으로 이미 데이터베이스에 존재하는 이메일을 요청으로 보냈을 때

code와 message에 에러가 반환되는 것을 볼 수 있다.

서버에서 ResponseEntity를 통해 오는 응답을 받을 DTO를 프론트에서 정의해준다.

타입스크립트의 장점을 살려 타입을 맞춰준다.

서버에서 오는 ResponseDto

enum을 통해 code를 매크로로 정의한다 (VO)

Code는 ResponseCode, message는 string 타입으로 형을 맞춰준다.

서버에서 json객체로 오는 response는 구조화 대입을 통해 RespoonseDto에 할당된다.

Servlet은 간단히 말하자면 CGI를 개선해서 동적 페이지를 생성해주기 위한 서버 프로그램, 자바 클래스이다. 추후에 다룰 예정이다.

클라이언트에서 요청은 Dispatcher Servlet이 먼저 처리한다. 프론트 컨트롤러 라고도 한다.

Dispatcher Servlet은 요청에 매핑된 적절한 Controller를 찾아 요청을 위임한다. 과거엔 Servlet이 web.xml에서 맵핑된 URL을 찾아주어야 했지만, Dispatcher Servlet을 통해 Controller만 적절히 구현해두면 알아서 적절하게 요청을 위임해주게 되었다.

 

  // Servlet alias
  <servlet>
  	<servlet-name>HelloServlet</servlet-name>
  	<servlet-class>test.servlet.HelloServlet</servlet-class>
  </servlet>
  
  // URL에 필요한 Servlet을 맵핑 시켜준다
  // /hello 경로로 요청이 오면 HelloServlet으로 요청 위임
  <servlet-mapping>
  	<servlet-name>HelloServlet</servlet-name>
  	<url-pattern>/hello</url-pattern>
  </servlet-mapping>

/hello 경로로 요청이 오면 HelloServlet을 통해 페이지를 제공하는 web.xml의 예시이다.

 

각 레이어 간 데이터 이동은 DTO(Data Transfer Object)를 이용한다.

DTO는 데이터 전송을 위해 getter, setter메소드만 가지고 비즈니스 로직을 가지지 않는 간단한 객체이다.

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class CommentListItem {
	private String nickname;
	private String profileImage;
	private String writeDatetime;
	private String content;
}

댓글에 관한 DTO

 

Controller는 위에서 말했듯이 요청에 대한 처리를 위임받아 적절한 처리를 한 후에 Response, 혹은 패턴에 따라 View에 넘겨주는 비즈니스 로직을 처리하는 객체이다.

@RestController
@RequestMapping("api/v1/auth")
@RequiredArgsConstructor
public class AuthController {
	
	private final AuthService authService;
	
	@PostMapping("/sign-up")
	public ResponseEntity<? super SignUpResponseDto> signUp(
		@RequestBody @Valid SignUpRequestDto requestBody
		) {
			ResponseEntity<? super SignUpResponseDto> response = authService.signUp(requestBody);
			return response;
	}	


	@PostMapping("/sign-in")
	public ResponseEntity<? super SignInResponseDto> signIn(
		@RequestBody @Valid SignInRequestDto requestBody
	){
		ResponseEntity<? super SignInResponseDto> response = authService.signIn(requestBody);
		return response;
	}
	
	
}

회원가입과 로그인에 관한 Controller 예시이다. 

Mapping 어노테이션을 통해 요청, 경로에 대한 맵핑을 하고 그에 따른 처리 코드를 작성한다.

위에서 xml 파일에서 URL 맵핑을 설정하는 과정과 비슷하다고 볼 수 있다.

예시를 살펴보자면

1. 로그인, 또는 회원가입에 필요한 데이터를 DTO 인자로 받는다. (requestBody)

2. service객체의 메소드를 호출해 데이터베이스 작업

3. ResponseEntity로 감싸서 response 반환

위의 그림(client, controller, service, repository) 에서 client <-> controller 까지의 과정이라고 볼 수 있다.

 

다음 글에서 controller 이후 계층에 관해서 작성해보겠다.

npx create-react-app projectName --template typescript

타입스크립트를 사용하는 리액트 프로젝트를 생성

 

 

package.json 과 함께 기본 디렉토리 구조가 생선된다.

 

public경로 안데 index.html 파일을 볼 수 있다.

 

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- 구글 inter font -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet">

    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

큰 내용 없는 html 파일이다.

중요한건 "root" id 를 달고 있는 div 태그이다.

이후에 살펴보겠지만 해당 요소에 리엑트 엘리먼트가 표시된다.

프로젝트 진행을 위한 디렉토리 구조이다.

 

index.tsx를 살펴보자

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

 

React와 ReactDom을 import 해준다.

getElementById()를 통해서 아까 index.html의 <div id ="root"> 요소를 가져온다.

createRoot 함수는 공식 문서를 참고하면

DOM 요소 내부에 콘텐츠를 표시하기 위한 React 루트를 생성한다.

간단히 말하자면 호출한 요소 자리에 랜더링 한다는 의미이다.

root.render 를 통해서 말이다. App.tsx를 import하여 랜더링 해주는 모습을 볼 수 있다.

즉, App.tsx에 화면에 나타날 컴포넌트들을 정의하면 되겠다.

 

App.tsx(예시)

//          component: Application 컴포넌트 			  //
function App() {
  return (
    <Routes>
      <Route element={<Container />}>
        <Route path={MAIN_PATH()} element={<Main />} / >
        <Route path={AUTH_PATH()} element={<Authentication />} / >
        <Route path={SEARCH_PATH(':searchWord')} element={<Search />} / >
        <Route path={USER_PATH(':userEmail')} element={<UserP />} / >
        <Route path={BOARD_PATH()}>
          <Route path={BOARD_WRITE_PATH()} element={<BoardWrite/>}/>
          <Route path={BOARD_DETAIL_PATH(':boardNumber')} element={<BoardDetail/>}/>
          <Route path={BOARD_UPDATE_PATH('boardNumber')} element={<BoardUpdate/>} />
        </Route> 
      <Route path="*" element={<h1>Not Found</h1>} />
      </Route>
    </Routes>
  );
}

export default App;

컴포넌트를 표시하고 라우트를 설정해주었다.

 

이외 디렉토리는 내가 만든 디렉토리이다.

간단한 설명

apis - 서버 요청에 대한 메소드 및 path 정리

components - 리액트 컴포넌츠

layouts - header 와 footer를 포함한 컨테이너

types - dto와 props 등의 데이터 타입 선언

views - 각 페이지 view

Spring initializr를 vs code 에서 명령어로 실행가능 하다.

  이후에 group id, artifact name 등을 설정해주면 된다.

 

패키지 dependencies를 설정해준다.

내가 고른 패키지의 간단한 설명

Spring Web(Spring MVC) -  web 프로젝트에 필요한 기능 제공, mvc패턴과 request mapping 등

Validation - 어노테이션을 통해 유효성을 검증할 수 있다. (requestBody 등)

Spring Security -보안 관련 메소드 제공

Spring Data JPA - Repository 인터페이스를 를 통해 데이터 베이스 CRUD작업 (JPA기반을 더 편하게)

MySQL Driver - 이름 그대로 Mysql 데이터베이스 드라이버

Lombok - 어노테이션을 통해 getter setter등을 편리하게 세팅

 

vscode 확장프로그램으로 다운받을 수 있다.

데이터베이스 ERD를 GUI로 직관적으로 생성 및 관리할 수 있다.

vuerd.json 형식의 파일을 생성해준 뒤

ERD Editor를 통해 열어준다

 

마우스로 편하게 ERD를 그리고 key constraints도 추가할 수 있다.

 

그리고 가장 강력한 기능이라고 할 수 있는 DDL 생성. 상단바에 SQL DDL을 클릭한다.

 

내가 그린 ERD에 맞춰서 SQL을 작성해준다.

'데이터베이스' 카테고리의 다른 글

코테 대비 MySQL  (0) 2024.03.12