Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

[비트캠프] 87일차(18주차5일) - Spring Framework: myapp-64 중간, 프로젝트 절차(Use-case 식별 Guide II) 본문

네이버클라우드 AIaaS 개발자 양성과정 1기/Spring Framework, Spring Boot

[비트캠프] 87일차(18주차5일) - Spring Framework: myapp-64 중간, 프로젝트 절차(Use-case 식별 Guide II)

끈기JK 2023. 3. 10. 12:00

 

Use-case 식별 - include / extend 관계

 

Use-case 식별 Guide II

① 여러 Use-case 에 중복되는 시나리오가 있다면 별도의 Use-case 로 추출하라.

게시글 등록하기, 게시글 변경하기, 게시글 삭제하기 에서 로그인을 《include》 한다. stereotype 이라 한다.

→ 로그인은 업무가 아니기 때문에 원래는 Use-case 가 될 수 없다.

 

② CRUD Use-case 들을 한 개의 Use-case 로 묶을 수 있다.

단, 묶은 Use-case 가 2주~4주 안에 개발 가능해야 한다.

게시글 등록하기, 게시글 조회하기, 게시글 변경하기, 게시글 삭제하기 를 묶어서 게시글 관리라 한다 → Handle Board, Manage Board

 

③ 서로 관련있는 Use-case 를 한 개의 Use-case 로 묶을 수 있다.

로그인 하기, 로그아웃 하기 → 사용자 인증

 

 

Use-case 에 정답은 없다. 이런게도 그릴수 있구나 정도로 알면 좋다. 아래에 그림을 추가하거나 텍스트를 넣어도 된다.

 

 

 

### 64. Back-end 와 Front-end 분리하기: 클라이언트 렌더링 방식으로 전환

 

RESTful 방식은 요청 주소가 아닌 요청 method 로 CRUD 를 구분한다.

 

REST API 요청 정보 사이트 'RFC9110' : https://www.rfc-editor.org/rfc/rfc9110.html#name-overview

 

insert 메서드는 @PostMapping, list 메서드는 @GetMapping, view 메서드는 @GetMapping("{no}") 로 요청 URL 의 게시물 번호를 @PathVariable 로 받는다.

update 메서드는 @PutMapping, delete 메서드는 @DeleteMapping("{no}") 로 동일하게 @PathVariable 로 받는다.

filedelete 메서드는 @PathVariable 로 게시물 번호, 파일 번호를 받는다.

package bitcamp.myapp.controller;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import bitcamp.myapp.service.BoardService;
import bitcamp.myapp.vo.Board;
import bitcamp.myapp.vo.BoardFile;
import bitcamp.myapp.vo.Member;
import bitcamp.util.ErrorCode;
import bitcamp.util.RestResult;
import bitcamp.util.RestStatus;

@RestController
@RequestMapping("/boards")
public class BoardController {

  Logger log = LogManager.getLogger(getClass());

  {
    log.trace("BoardController 생성됨!");
  }

  @Autowired private ServletContext servletContext;
  @Autowired private BoardService boardService;

  @PostMapping
  public Object insert(
      Board board,
      List<MultipartFile> files,
      HttpSession session) throws Exception{

    Member loginUser = (Member) session.getAttribute("loginUser");

    Member writer = new Member();
    writer.setNo(loginUser.getNo());
    board.setWriter(writer);

    List<BoardFile> boardFiles = new ArrayList<>();
    for (MultipartFile file : files) {
      if (file.isEmpty()) {
        continue;
      }

      String filename = UUID.randomUUID().toString();
      file.transferTo(new File(servletContext.getRealPath("/board/upload/" + filename)));

      BoardFile boardFile = new BoardFile();
      boardFile.setOriginalFilename(file.getOriginalFilename());
      boardFile.setFilepath(filename);
      boardFile.setMimeType(file.getContentType());
      boardFiles.add(boardFile);
    }
    board.setAttachedFiles(boardFiles);

    boardService.add(board);

    return new RestResult()
        .setStatus(RestStatus.SUCCESS);
  }

  @GetMapping
  public Object list(String keyword) {
    log.debug("BoardController.list() 호출됨!");

    // MappingJackson2HttpMessageConverter 가 jackson 라이브러리를 이용해
    // 자바 객체를 JSON 문자열로 변환하여 클라이언트로 보낸다.
    // 이 컨버터를 사용하면 굳이 UTF-8 변환을 설정할 필요가 없다.
    // 즉 produces = "application/json;charset=UTF-8" 를 설정하지 않아도 된다.
    return new RestResult()
        .setStatus(RestStatus.SUCCESS)
        .setData(boardService.list(keyword));
  }

  @GetMapping("{no}")
  public Object view(@PathVariable int no) {
    Board board = boardService.get(no);
    if (board != null) {
      return new RestResult()
          .setStatus(RestStatus.SUCCESS)
          .setData(board);
    } else {
      return new RestResult()
          .setStatus(RestStatus.FAILURE)
          .setErrorCode(ErrorCode.rest.NO_DATA);
    }
  }

  @PutMapping
  public Object update(
      Board board,
      List<MultipartFile> files,
      HttpSession session) throws Exception {

    Member loginUser = (Member) session.getAttribute("loginUser");

    Board old = boardService.get(board.getNo());
    if (old.getWriter().getNo() != loginUser.getNo()) {
      return new RestResult()
          .setStatus(RestStatus.FAILURE)
          .setErrorCode(ErrorCode.rest.UNAUTHORIZED)
          .setData("권한이 없습니다.");
    }

    List<BoardFile> boardFiles = new ArrayList<>();
    for (MultipartFile file : files) {
      if (file.isEmpty()) {
        continue;
      }

      String filename = UUID.randomUUID().toString();
      file.transferTo(new File(servletContext.getRealPath("/board/upload/" + filename)));

      BoardFile boardFile = new BoardFile();
      boardFile.setOriginalFilename(file.getOriginalFilename());
      boardFile.setFilepath(filename);
      boardFile.setMimeType(file.getContentType());
      boardFile.setBoardNo(board.getNo());
      boardFiles.add(boardFile);
    }
    board.setAttachedFiles(boardFiles);

    boardService.update(board);

    return new RestResult()
        .setStatus(RestStatus.SUCCESS);
  }

  @DeleteMapping("{no}")
  public Object delete(@PathVariable int no, HttpSession session) {
    Member loginUser = (Member) session.getAttribute("loginUser");

    Board old = boardService.get(no);
    if (old.getWriter().getNo() != loginUser.getNo()) {
      return new RestResult()
          .setStatus(RestStatus.FAILURE)
          .setErrorCode(ErrorCode.rest.UNAUTHORIZED)
          .setData("권한이 없습니다.");
    }
    boardService.delete(no);

    return new RestResult()
        .setStatus(RestStatus.SUCCESS);
  }

  @DeleteMapping("{boardNo}/files/{fileNo}")
  public Object filedelete(
      @PathVariable int boardNo,
      @PathVariable int fileNo,
      HttpSession session) {
    Member loginUser = (Member) session.getAttribute("loginUser");
    Board old = boardService.get(boardNo);

    if (old.getWriter().getNo() != loginUser.getNo()) {
      return new RestResult()
          .setStatus(RestStatus.FAILURE)
          .setErrorCode(ErrorCode.rest.UNAUTHORIZED)
          .setData("권한이 없습니다.");

    } else {
      boardService.deleteFile(fileNo);
      return new RestResult()
          .setStatus(RestStatus.SUCCESS);
    }
  }

}

 

학생관리 접속 주소를 .html 로 변경한다.

로그인과 로그아웃의 메뉴를 다르게 보인다.

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>비트캠프 - NCP 1기</title>
<style>
.login {
  display: none;
}
.logout {
  display: none;
}
</style>
</head>
<body>
<h1>강의관리시스템!</h1>
<ul>
  <li><a href="student/index.html">학생관리</a></li>
  <li><a href="admin/teacher/list">강사관리</a></li>
  <li><a href="board/list.html">게시판</a></li>
  
  <li class="login"><a href="auth/form.html">로그인</a></li>

  <li class="logout"><a href="app/auth/logout" onclick="logout(); return false;">
    로그아웃(<span id="username"></span>)</a></li>
</ul>

<script>

fetch('app/auth/user')
.then(response => {
  return response.json();
})
.then(result => {
  if (result.status == 'success') {
    document.querySelector('#username').innerHTML = result.data.name;
    document.querySelector('.logout').classList.remove('logout');
  } else {
    document.querySelector('.login').classList.remove('login');
  }
})
.catch(exception => {
  alert("로그인 사용자 정보 조회 오류!");
})

function logout() {
  fetch('app/auth/logout')
  .then(response => {
    return response.json();
  })
  .then(result => {
    location.reload();
  })
  .catch(exception => {
    console.log(exception);
  });
}
</script>


</body>
</html>

 

쿠키에서 값 꺼내와서 email input box 에 넣는다.

로그인 클릭시 saveEmail 체크되어 있으면 쿠키에 이메일 보관한다.

getCookie(...), setCookie(...) 는 검색해서 가져왔다.

<!-- auth/form.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>비트캠프 - NCP 1기</title>
</head>
<body>
<h1>로그인</h1>

<form id="login-form" action="login" method="post">
<table border="1">
<tr>
  <th>회원 유형</th>
  <td>
    <input type="radio" name="usertype" value="student" checked> 학생
    <input type="radio" name="usertype" value="teacher"> 강사
  </td>
</tr>
<tr>
  <th>이메일</th>
  <td><input type="email" name="email" 
      value=""></td>
</tr>
<tr>
  <th>암호</th>
  <td><input type="password" name="password"></td>
</tr>
</table>

<div>
  <input type="checkbox"> 이메일 저장<br>
  <button id="btn-login" type="button">로그인</button>
</div>
</form>

<script>

document.querySelector('input[name="email"]').value = getCookie('email');

document.querySelector('#btn-login').onclick = () => {
  // 이메일을 쿠키에 보관
  if (document.querySelector('input[type="checkbox"]:checked') != null) {
    setCookie('email', document.querySelector('input[name="email"]').value, 60 * 60 * 24 * 7);
  } else {
	  setCookie('email', '', 0);
  }
	
  const form = document.querySelector('#login-form');
  const formData = new FormData(form);
  
  fetch("../app/auth/login", {
    method: "post",
    body: formData
  })
  .then(response => {
    return response.json();
  })
  .then(result => {
    if (result.status == 'success') {
      location.href = '../';
    } else {
      alert('로그인 실패!');
      document.querySelector('input[name="email"]').value = "";
      document.querySelector('input[name="password"]').value = "";
    }
  })
  .catch(exception => {
    alert("로그인 오류!");
    console.log(exception);
  });
};

function getCookie(cookieName){
    var cookieValue=null;
    if(document.cookie){
        var array=document.cookie.split((escape(cookieName)+'='));
        if(array.length >= 2){
            var arraySub=array[1].split(';');
            cookieValue=unescape(arraySub[0]);
        }
    }
    return cookieValue;
}
 
function setCookie(cookieName, cookieValue, cookieMaxAge, cookiePath, cookieDomain, cookieSecure){
    var cookieText=encodeURIComponent(cookieName)+'='+encodeURIComponent(cookieValue);
    cookieText+=(cookieMaxAge ? '; max-age='+cookieMaxAge : '');
    cookieText+=(cookiePath ? '; path='+cookiePath : '');
    cookieText+=(cookieDomain ? '; domain='+cookieDomain : '');
    cookieText+=(cookieSecure ? '; secure' : '');
    document.cookie=cookieText;
}
</script>
</body>
</html>

 

클릭시 한 화면에서 볼 수 있게 바꾼다.

맨 처음 학생 목록을 fetch(...) 로 가져온다.

클릭시 우측에 상세정보 나오도록 한다.

변경 클릭시 json 으로 변환해서 업데이트 한다.

<!-- student/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프 - NCP 1기</title>
<style>
aside {
  border: 1px solid red;
  box-sizing: border-box;
  background-color: yellow; 
  float: left;
  width: 200px;
}

article {
  border: 1px solid black;
  box-sizing: border-box;
  margin-left: 210px;
}

#student-table > tbody > tr:hover { 
  background-color: navy;
  color: white;
}
</style>
</head>
<body>
<h1>학생(Thymeleaf)</h1>

<aside> 
<form action='list' method='get'>
  <input type='text' name='keyword' value="">
  <button>검색</button>
</form>
<div><a href='form'>새 학생</a></div>
<table id="student-table" border='1'>
<thead>
  <tr>
    <th>번호</th> <th>이름</th> <th>재직</th>
  </tr>
</thead>
<tbody></tbody>
</table>
</aside>

<article>
  <form id='student-form' action='update' method='post'>
  <table border='1'>
  <tr>
    <th>번호</th>
    <td><input id="f-no" type='text' name='no' readonly></td>
  </tr>
  <tr>
    <th>이름</th>
    <td><input id="f-name" type='text' name='name'></td>
  </tr>
  <tr>
    <th>이메일</th>
    <td><input id="f-email" type='email' name='email'></td>
  </tr>
  <tr>
    <th>암호</th>
    <td><input id="f-password" type='password' name='password'></td>
  </tr>
  <tr>
    <th>전화</th>
    <td><input id="f-tel" type='tel' name='tel'></td>
  </tr>
  <tr>
    <th>우편번호</th>
    <td><input id="f-postNo" type='text' name='postNo'></td>
  </tr>
  <tr>
    <th>기본주소</th>
    <td><input id="f-basicAddress" type='text' name='basicAddress'></td>
  </tr>
  <tr>
    <th>상세주소</th>
    <td><input id="f-detailAddress" type='text' name='detailAddress'></td>
  </tr>
  <tr>
    <th>재직여부</th>
    <td><input id="f-working" type='checkbox' name='working' value="true"> 재직중</td>
  </tr>
  <tr>
    <th>성별</th>
    <td><input type='radio' name='gender' value='M'> 남
    <input type='radio' name='gender' value='W' checked> 여</td>
  </tr>
  <tr>
    <th>전공</th>
    <td><select id="f-level" name='level'>
      <option value='0'>비전공자</option>
      <option value='1'>준전공자</option>
      <option value='2'>전공자</option>
    </select></td>
  </tr>
  <tr>
    <th>등록일</th>
    <td><span id="f-createdDate"></span></td>
  </tr>
  </table>

  <div>
    <button id='btn-insert' type='button'>추가</button>
    <button id="btn-update" type="button">변경</button>
    <button id='btn-delete' type='button'>삭제</button>
  </div>
  </form>
</article>

<script>
fetch("../admin/students")
.then(response => {
  return response.json();
})
.then(result => {
  let tbody = "";
  result.data.forEach(student => {
    let html = `
      <tr data-no="${student.no}" onclick="getStudent(event)">
         <td>${student.no}</td> 
         <td>${student.name}</td> 
         <td>${student.working ? '예' : '아니오'}</td> 
      </tr>
    `;
    tbody += html;
  });
  document.querySelector("#student-table > tbody").innerHTML = tbody;
});

function getLevelTitle(level) {
  switch (level) {
    case 0: return "비전공자";
    case 1: return "준전공자";
    case 2: return "전공자";
    default: return "기타";
  }
}

function getStudent(e) {
  let no = e.currentTarget.getAttribute('data-no');
  
  fetch('../admin/students/' + no)
  .then(response => {
    return response.json();
  })
  .then(result => {
    if (result.status == 'failure') {
      alert('학생을 조회할 수 없습니다.');
      return;
    }
    
    let student = result.data;
    console.log(student);
    document.querySelector('#f-no').value = student.no;
    document.querySelector('#f-name').value = student.name;
    document.querySelector('#f-email').value = student.email;
    document.querySelector('#f-tel').value = student.tel;
    document.querySelector('#f-postNo').value = student.postNo;
    document.querySelector('#f-basicAddress').value = student.basicAddress;
    document.querySelector('#f-detailAddress').value = student.detailAddress;
    document.querySelector('#f-working').checked = student.working;
    document.querySelector(`input[name="gender"][value="${student.gender}"]`).checked = true;
    document.querySelector('#f-level').value = student.level;
    document.querySelector('#f-createdDate').innerHTML = student.createdDate;
  });
}

document.querySelector('#btn-insert').onclick = () => {
  console.log('추가');
};

document.querySelector('#btn-update').onclick = () => {
  const form = document.querySelector('#student-form');
  const formData = new FormData(form);
  
  // FormData ==> Query String
  // 방법1) 
  // let qs = [...formData.entries()].map(x => `${encodeURIComponent(x[0])}=${encodeURIComponent(x[1])}`).join('&');
  // 방법2)
  //let qs = new URLSearchParams(formData).toString();
  //console.log(qs);
  
  let json = JSON.stringify(Object.fromEntries(formData)); 
  //console.log(json);
  
  fetch("../admin/students", {
    method: "PUT",
    headers: {
        "Content-Type": "application/json"
        //"Content-Type": "application/x-www-form-urlencoded"
    },
    //body: formData
    body: json
    //body: qs
  })
  .then(response => {
    return response.json();
  })
  .then(result => {
	  //console.log(result);
    if (result.status == 'success') {
      alert("변경 했습니다.");
      location.reload();
    } else {
      alert('변경 실패!');
      console.log(result.data);
    }
  })
  .catch(exception => {
    alert('변경 중 오류 발생!');
    console.log(exception);
  });

};

document.querySelector('#btn-delete').onclick = () => {
  console.log('삭제');
};

// entries ==> query string
function toQueryStringFromEntries(entries) {
  let qs = "";
  for (let [key, value] of entries) {
    if (qs.length > 0) {
      qs += "&";
    }
    qs += encodeURIComponent(key) + "=" + encodeURIComponent(value);
  }
  return qs;
}

function toQueryStringFromEntries2(entries) {
  let arr = [];
  for (let entry of entries) {
    arr.push(entry);
  }
  
  //console.log(arr);
  
  let arr2 = arr.map(x => `${encodeURIComponent(x[0])}=${encodeURIComponent(x[1])}`);
  //console.log(arr2);
  
  let str = arr2.join('&');
  //console.log(str);
  
  return str;
}

function toQueryStringFromEntries3(entries) {
  let arr = [...entries];
  
  //console.log(arr);
  
  let arr2 = arr.map(x => `${encodeURIComponent(x[0])}=${encodeURIComponent(x[1])}`);
  //console.log(arr2);
  
  let str = arr2.join('&');
  //console.log(str);
  
  return str;
	}

</script>

</body>
</html>

 

arr 에 entry 인 배열을 push 한다. 이를 map() 이용하여 URI 에 사용하는 문자열로 변환한다. join('&') 로 묶는다.

 

 

 

 

chatGPT 로 부가 설명 확인

 

Spring 에서 @Autowired 동작 원리는?

Spring 프레임워크에서 @Autowired는 의존성 주입(Dependency Injection)을 위해 사용되는 어노테이션입니다. @Autowired 어노테이션을 사용하면 Spring은 해당 클래스의 객체를 생성하고 관리하는 Bean 컨테이너에서 자동으로 의존성을 주입합니다.

즉, @Autowired 어노테이션을 사용하면, 스프링은 해당 클래스 타입의 빈을 찾아서 객체를 생성하고, 해당 객체의 필드나 생성자 등에서 해당 빈을 자동으로 주입해 줍니다.

따라서, @Autowired private StudentService studentService 라는 코드에서는, Spring 컨테이너에서 StudentService 클래스 타입의 빈을 찾아서 studentService 필드에 자동으로 주입해줍니다.

이때, 주입되는 객체는 기본적으로 Singleton으로 생성되며, 스프링이 관리하는 Bean 컨테이너에서만 동작합니다. 즉, StudentService 클래스의 객체가 필요한 곳에서는 @Autowired 어노테이션만 사용하면 되며, 객체 생성과 메모리 관리에 대한 부분은 Spring 프레임워크가 자동으로 처리해줍니다.

이와 같이 스프링이 객체를 생성하고 주입하는 것을 의존성 주입(Dependency Injection)이라고 합니다. 이를 통해 코드의 유지보수성이 향상되며, 객체 간의 결합도가 낮아져 더 유연하고 테스트하기 쉬운 코드를 작성할 수 있습니다.

 

 

Spring 에서 @Service 의미를 알려줘

@Service 어노테이션은 Spring Framework에서 비즈니스 로직 처리를 담당하는 Service 클래스에 부여하는 어노테이션입니다. @Service 어노테이션을 부여함으로써 해당 클래스를 Spring 컨테이너에서 Bean으로 등록하며, 이를 통해 IoC(Inversion of Control) 및 DI(Dependency Injection)를 지원합니다.

위의 코드에서 @Service 어노테이션은 DefaultBoardService 클래스가 BoardService 인터페이스를 구현한 비즈니스 로직을 처리하는 클래스임을 표시합니다. 이렇게 @Service 어노테이션이 부여된 클래스는 Spring이 Bean으로 등록되며, 다른 클래스에서 @Autowired 어노테이션을 사용하여 해당 클래스의 인스턴스를 주입받을 수 있습니다.

따라서, DefaultBoardService 클래스에 @Service 어노테이션이 부여된 것은 해당 클래스가 BoardService 인터페이스를 구현하는 비즈니스 로직을 처리하는 Service 클래스임을 나타내며, Spring에서 Bean으로 등록하여 IoC와 DI를 지원한다는 것을 의미합니다.

즉, DefaultBoardService 클래스는 Spring 컨테이너에서 생성 및 관리되는 Bean으로 등록되어, 다른 클래스에서 @Autowired 어노테이션을 사용하여 인스턴스를 주입받을 수 있으며, 이를 통해 객체 간의 결합도를 낮추고 코드의 유지보수성을 향상시킬 수 있습니다.

 

 

javascript 의 URLSearchParams() 에 대해 알려줘

URLSearchParams는 JavaScript에서 URL 쿼리 문자열(query string)을 쉽게 다룰 수 있는 API입니다. URLSearchParams를 사용하면, URL 쿼리 문자열을 객체화하여 다양한 방식으로 처리할 수 있습니다.

URLSearchParams 객체는 다음과 같이 생성할 수 있습니다.
const params = new URLSearchParams(window.location.search);​

위의 코드에서 window.location.search는 현재 페이지의 URL에서 쿼리 문자열 부분을 가져옵니다. new URLSearchParams()는 이 쿼리 문자열을 기반으로 URLSearchParams 객체를 생성합니다.

생성된 params 객체는 다음과 같은 메서드들을 지원합니다.

- append(name, value): 지정된 이름과 값을 가진 파라미터를 쿼리 문자열에 추가합니다.
- delete(name): 지정된 이름을 가진 파라미터를 쿼리 문자열에서 제거합니다.
- get(name): 지정된 이름을 가진 파라미터의 값을 가져옵니다.
- getAll(name): 지정된 이름을 가진 모든 파라미터의 값을 배열로 반환합니다.
- has(name): 지정된 이름을 가진 파라미터가 있는지 여부를 확인합니다.
- set(name, value): 지정된 이름의 파라미터 값을 지정된 값으로 설정합니다.
- sort(): 쿼리 문자열 내 파라미터를 알파벳순으로 정렬합니다.
- toString(): URLSearchParams 객체를 쿼리 문자열로 변환합니다.

예를 들어, URL이 https://example.com/?q=javascript&lang=en인 경우, 다음과 같이 URLSearchParams 객체를 활용할 수 있습니다.
const params = new URLSearchParams(window.location.search);
console.log(params.get('q')); // 'javascript'
console.log(params.get('lang')); // 'en'
params.append('page', 1);
console.log(params.toString()); // 'q=javascript&lang=en&page=1'

이와 같이 URLSearchParams를 활용하여 쿼리 문자열을 쉽게 다룰 수 있으며, 이를 통해 다양한 기능을 구현할 수 있습니다.

 

 

javascript 에서 URLSearchParams(formData) 처럼 사용시 동작 원리는?

URLSearchParams 생성자에 FormData 객체를 전달하면, FormData 객체에 담긴 데이터를 쿼리 문자열로 변환하여 URLSearchParams 객체를 생성합니다. 이렇게 생성된 URLSearchParams 객체를 사용하면, URL 쿼리 문자열을 쉽게 다룰 수 있습니다.

예를 들어, 다음과 같이 HTML 폼(form) 요소를 생성하고, 이를 통해 FormData 객체를 생성한 후, 이를 URLSearchParams 생성자에 전달하여 URLSearchParams 객체를 생성할 수 있습니다.

<form id="myForm">
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" value="John Doe">
  <br>
  <label for="email">Email:</label>
  <input type="email" id="email" name="email" value="johndoe@example.com">
  <br>
  <button type="submit">Submit</button>
</form>​
const form = document.querySelector('#myForm');
const formData = new FormData(form);
const params = new URLSearchParams(formData);

console.log(params.toString()); // 'name=John+Doe&email=johndoe%40example.com'

 

위의 코드에서, FormData 객체는 폼(form) 요소에서 입력된 데이터를 담고 있으며, 이를 URLSearchParams 생성자에 전달하여 URLSearchParams 객체를 생성합니다. params.toString()은 생성된 URLSearchParams 객체를 쿼리 문자열로 변환하여 출력합니다.

이와 같이 FormData 객체를 URLSearchParams 생성자에 전달하면, 서버로 데이터를 전송할 때 URL 쿼리 문자열을 활용하여 전송할 수 있습니다.

 

 

자바스크립트에서 JSON.stringify(Object.fromEntries(formData)) 사용하면?

FormData 객체는 HTML 폼(form) 요소에서 입력된 데이터를 담고 있는 객체입니다. 이를 JSON 문자열로 변환하기 위해 JSON.stringify(Object.fromEntries(formData))와 같은 코드를 사용할 수 있습니다.

Object.fromEntries()는 ECMAScript 2019부터 지원되는 메서드로, 이전에는 Object.entries() 메서드와 함께 사용하여 객체를 배열로 변환해야 했습니다. 하지만 Object.fromEntries()를 사용하면 배열을 다시 객체로 변환할 수 있습니다.

따라서 Object.fromEntries(formData)는 FormData 객체에서 가져온 데이터를 객체 형태로 변환하는 것이며, 이를 JSON.stringify() 메서드로 JSON 문자열로 변환할 수 있습니다.

예를 들어, 다음과 같은 HTML 폼(form) 요소가 있다고 가정해보겠습니다.
<form id="myForm">
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" value="John Doe">
  <br>
  <label for="email">Email:</label>
  <input type="email" id="email" name="email" value="johndoe@example.com">
  <br>
  <button type="submit">Submit</button>
</form>​

다음과 같은 자바스크립트 코드를 사용하면, FormData 객체에서 가져온 데이터를 JSON 문자열로 변환할 수 있습니다.
const form = document.querySelector('#myForm');
const formData = new FormData(form);
const jsonData = JSON.stringify(Object.fromEntries(formData));

console.log(jsonData); // {"name":"John Doe","email":"johndoe@example.com"}​

위의 코드에서 Object.fromEntries(formData)는 FormData 객체에서 가져온 데이터를 객체 형태로 변환하며, JSON.stringify() 메서드로 이를 JSON 문자열로 변환합니다. 이렇게 변환된 JSON 문자열은 서버로 데이터를 전송할 때 사용할 수 있습니다.

 

 

자바스크립트에서 encodeURIComponent(value) 함수의 역할은?

JavaScript의 encodeURIComponent(value) 함수는 주어진 문자열 value을 인코딩하여 URI(Uniform Resource Identifier) 구성 요소로 사용할 수 있는 형식으로 변환합니다.

URI 구성 요소는 URI의 구성 요소 중 일부이며, 예를 들어 쿼리 문자열 매개 변수의 값이나 URL 경로 구성 요소 등을 포함합니다. URI 구성 요소에는 일부 문자가 특별한 의미를 갖거나 URI를 유효하지 않게 만들 수 있는 문자가 있기 때문에, 이러한 문자들을 인코딩해야 합니다.

encodeURIComponent(value) 함수는 value 문자열에서 URI 구성 요소로 인코딩되어야 하는 모든 문자를 인코딩하며, % 문자로 시작하는 인코딩된 값을 반환합니다. 이 함수를 사용하여 URI 매개 변수에 대한 안전한 값을 만들 수 있습니다.

예를 들어, encodeURIComponent('Hello, World!')는 Hello%2C%20World%21을 반환합니다.

 

 

 


 

 

조언

 

*

 

 


 

과제

 

/