[스파르타코딩클럽] 웹개발의 봄, Spring 3주차

웹개발의 봄, Spring 3주차

💡기대하는 것

웹개발의 봄, Spring 강의를 통해 웹의 기본 구조를 배우고 간단한 서비스를 구현
개발부터 배포까지 과정을 경험해보면서 웹서비스의 전체적인 틀을 잡고 싶다.

📝배운것

  • End to End 로 타임라인 서비스 프로젝트 완성
    http://spring.spartacodingclub.kr/timeline

  • API를 먼저 설계 후 그에 따라 코드를 작성하자

전체 흐름 다시 잡기

  • Controller - Service - Repository 3계층

    안에서 바깥으로, 즉 Repository 쪽부터 Service, Controller 방향으로 만들어나가자

✏️API 설계하기

CRUD 설계

기능METHODURLRETURN
메모 생성하기POST/api/memosMemo
메모 조회하기GET/api/memosList<Memo>
메모 변경하기PUT/api/memos/{id}Long
메모 삭제하기DELETE/api/memos/{id}Long

✏️Repository 만들기

  • Memo 클래스(엔터티) 생성
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Memo extends Timestamped{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String contents;

    public void update(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }

    public Memo(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }
}
  • Timestapmed 추상클래스 생성 (공통 컬럼)
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime modifiedAt;
}
  • MemoRepository 생성
public interface MemoRepository extends JpaRepository<Memo, Long> {

    List<Memo> findAllByOrderByModifiedAtDesc();
}

List<Memo> findAllByOrderByModifiedAtDesc() : ModifiedAt컬럼을 기준으로 정렬

쿼리 메소드

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods

  • MemoRequestDto 생성
@Getter
public class MemoRequestDto {

    private String username;
    private String contents;
}

✏️Service 만들기

  • MemoService 생성
@RequiredArgsConstructor
@Service
public class MemoService {

    private final MemoRepository memoRepository;

    @Transactional
    public Long update(Long id, MemoRequestDto requestDto) {
        Memo memo = memoRepository.findById(id).orElseThrow(
                () -> new NullPointerException("해당 아이디가 존재하지 않습니다.")
        );

            memo.update(requestDto);
            return id;
    }
}

✏️Controller 만들기

설계한 API 대로 Controller를 만들어준다.

  • MemoController 생성
@RequiredArgsConstructor
@RestController
public class MemoController {

    private final MemoRepository memoRepository;
    private final MemoService memoService;

    @GetMapping("/api/memos")
    public List<Memo> getMemos() {
        return memoRepository.findAllByOrderByModifiedAtDesc();
    }

    @PostMapping("/api/memos")
    public Memo createMemo(@RequestBody MemoRequestDto requestDto) {
        Memo memo = new Memo(requestDto);

        return memoRepository.save(memo);
    }

    @PutMapping("/api/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        return memoService.update(id, requestDto);
    }

    @DeleteMapping("/api/memos/{id}")
    public Long deleteMemo(@PathVariable Long id){
        memoRepository.deleteById(id);
        return id;
    }

}

ARC를 통해서 API 잘 동작하는지 확인


✏️HTML, CSS, JS 만들기

자바스크립트 백틱 연습

let name = '내 이름';
let text = `${name}님의 스프링 5주 완주를 축하합니다!`;
console.log(text);

jQuery 연습

# 인풋값 가져오기
$('#post-url').val();

# 인풋에 값 넣어주기
$('#post-url').val('new text');

# HTML 없애기
$('#cards-box').empty();

# HTML 추가하기
$('#cards-box').append(`<div class="card">
  <img class="card-img-top"
       src="https://www.eurail.com/content/dam/images/eurail/Italy%20OCP%20Promo%20Block.adaptive.767.1535627244182.jpg"
       alt="Card image cap">
  <div class="card-body">
      <a href="#" class="card-title">여기 기사 제목이 들어가죠</a>
      <p class="card-text">기사의 요약 내용이 들어갑니다. 동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라만세 무궁화 삼천리 화려강산...</p>
      <p class="card-text comment">여기에 코멘트가 들어갑니다.</p>
  </div>
</div>`);

✏️클라이언트 설계하기

필요한 기능 살펴보기

  • 접속하자마자 메모 전체 목록 조회하기
    1. GET API 사용해서 메모 목록 불러오기
    2. 메모 마다 HTML 만들고 붙이기
  • 메모 생성하기
    1. 사용자가 입력한 메모 내용 확인하기
    2. POST API 사용해서 메모 신규 생성하기
    3. 화면 새로고침하여 업데이트 된 메모 목록 확인하기
  • 메모 변경하기
    1. 사용자가 클릭한 메모가 어떤 것인지 확인
    2. 변경한 메모 내용 확인
    3. PUT API 사용해서 메모 내용 변경하기
    4. 화면 새로고침하여 업데이트 된 메모 목록 확인하기
  • 메모 삭제하기
    1. 사용자가 클릭한 메모가 어떤 것인지 확인
    2. DELETE API 사용해서 메모 삭제하기
    3. 화면 새로고침하여 업데이트 된 메모 목록 확인하기

✏️메모 생성하기

// 메모를 생성합니다.
function writePost() {
  // 1. 작성한 메모를 불러옵니다.
  let contents = $('#contents').val();

  // 2. 작성한 메모가 올바른지 isValidContents 함수를 통해 확인합니다.
  if (isValidContents(contents) == false) {
      return;
  }
  // 3. genRandomName 함수를 통해 익명의 username을 만듭니다.
  let username = genRandomName(10);

  // 4. 전달할 data JSON으로 만듭니다.
  let data = {'username': username, 'contents': contents};

  // 5. POST /api/memos 에 data를 전달합니다.
  $.ajax({
      type: "POST",
      url: "/api/memos",
      contentType: "application/json",
      data: JSON.stringify(data),
      success: function (response) {
          alert('메시지가 성공적으로 작성되었습니다.');
          window.location.reload();
      }
  });
}

✏️메모 조회하기

// 메모를 불러와서 보여줍니다.
function getMessages() {
  // 1. 기존 메모 내용을 지웁니다.
  $('#cards-box').empty();
  // 2. 메모 목록을 불러와서 HTML로 붙입니다.
  $.ajax({
      type: 'GET',
      url: '/api/memos',
      success: function (response) {
          for (let i = 0; i < response.length; i++) {
              let message = response[i];
              let id = message['id'];
              let username = message['username'];
              let contents = message['contents'];
              let modifiedAt = message['modifiedAt'];
              addHTML(id, username, contents, modifiedAt);
          }
      }
  })
}

// 메모 하나를 HTML로 만들어서 body 태그 내 원하는 곳에 붙입니다.
function addHTML(id, username, contents, modifiedAt) {
  // 1. HTML 태그를 만듭니다.
  let tempHtml = `<div class="card">
      <!-- date/username 영역 -->
      <div class="metadata">
          <div class="date">
              ${modifiedAt}
          </div>
          <div id="${id}-username" class="username">
              ${username}
          </div>
      </div>
      <!-- contents 조회/수정 영역-->
      <div class="contents">
          <div id="${id}-contents" class="text">
              ${contents}
          </div>
          <div id="${id}-editarea" class="edit">
              <textarea id="${id}-textarea" class="te-edit" name="" id="" cols="30" rows="5"></textarea>
          </div>
      </div>
      <!-- 버튼 영역-->
      <div class="footer">
          <img id="${id}-edit" class="icon-start-edit" src="images/edit.png" alt="" onclick="editPost('${id}')">
          <img id="${id}-delete" class="icon-delete" src="images/delete.png" alt="" onclick="deleteOne('${id}')">
          <img id="${id}-submit" class="icon-end-edit" src="images/done.png" alt="" onclick="submitEdit('${id}')">
      </div>
  </div>`;
  // 2. #cards-box 에 HTML을 붙인다.
  $('#cards-box').append(tempHtml);
}

✏️메모 변경하기

// 메모를 수정합니다.
function submitEdit(id) {
  // 1. 작성 대상 메모의 username과 contents 를 확인합니다.
  let username = $(`#${id}-username`).text().trim();
  let contents = $(`#${id}-textarea`).val().trim();

  // 2. 작성한 메모가 올바른지 isValidContents 함수를 통해 확인합니다.
  if (isValidContents(contents) == false) {
      return;
  }

  // 3. 전달할 data JSON으로 만듭니다.
  let data = {'username': username, 'contents': contents};

  // 4. PUT /api/memos/{id} 에 data를 전달합니다.
  $.ajax({
      type: "PUT",
      url: `/api/memos/${id}`,
      contentType: "application/json",
      data: JSON.stringify(data),
      success: function (response) {
          alert('메시지 변경에 성공하였습니다.');
          window.location.reload();
      }
  });
}

✏️메모 삭제하기

function deleteOne(id) {
  $.ajax({
      type: "DELETE",
      url: `/api/memos/${id}`,
      success: function (response) {
          alert('메시지 삭제에 성공하였습니다.');
          window.location.reload();
      }
  })
}

좋은 웹페이지 즐겨찾기