복습

sungsimdangmascot·2026년 4월 27일

1. Map을 DTO로 변환하기

Error: no suitable constructor found for Article(java.util.Map)

이 에러는 왜 발생하는 것이며, 어떻게 해결해야 할까?

검은 비닐 보따리와 예쁜 규격 상자

창고지기(Dao)가 지하 창고(DB)에서 데이터를 꺼내올 때, 처음부터 예쁜 상자에 담아오지 않는다. 일단 급한 대로 Map이라는 '검은 비닐 보따리'에 이름표(Key)와 내용물(Value)을 대충 쑤셔 담아서 올라온다.

하지만 이 보따리를 프로그램 이곳저곳에서 들고 다니기엔 너무 불편하다. 그래서 이 보따리 안의 내용물을 꺼내서 Article이라는 '예쁜 규격 상자(DTO)'에 옮겨 담는 작업이 필요하다.

위의 에러는 "네가 보따리(Map)를 던져줬는데, 우리 공장(Article)에는 보따리를 통째로 받아서 상자로 만들어주는 전용 조립 기계(생성자)가 없다!"라며 자바가 파업을 선언한 것이다.

해결책: 전용 조립 기계(생성자) 만들기

이 문제를 해결하려면 Article 클래스 내부에 Map을 재료로 받는 생성자를 추가해 주면 된다.

public class Article {
  private int id;
  private String title;
  private String body;
  // ... 생략 ...

  // 보따리(Map)를 통째로 받는 전용 조립 기계
  public Article(Map<String, Object> articleMap) {
    this.id = (int) articleMap.get("id");
    this.title = (String) articleMap.get("title");
    this.body = (String) articleMap.get("body");
  }
}

이 코드는 다음과 같은 3단계로 작동한다.
1. 꺼내기 (get): 보따리(articleMap)에 손을 넣어 'id'라는 이름표가 붙은 물건을 꺼낸다.
2. 도장 찍기 ((int)): 자바에게 "방금 꺼낸 건 의심할 여지 없는 숫자(int)야!"라고 강제로 확인 도장(형변환/Casting)을 찍는다.
3. 내 상자에 넣기 (this.id =): 지금 조립 중인 내 예쁜 상자(this)의 id 칸에 쏙 집어넣는다.

추가 예시: Member(회원) 상자 조립기

이 원리는 게시글(Article)뿐만 아니라 회원(Member), 댓글(Reply) 등 모든 데이터에 똑같이 적용된다. 만약 지하 창고에서 회원 정보를 보따리로 가져왔다면, Member 클래스에도 아래와 같은 기계를 만들어주면 끝이다.

public class Member {
  private int id;
  private String loginId;
  private String name;

  // 회원 보따리(Map) 전용 조립 기계
  public Member(Map<String, Object> memberMap) {
    this.id = (int) memberMap.get("id");
    this.loginId = (String) memberMap.get("loginId");
    this.name = (String) memberMap.get("name");
  }
}

2. 게시판 아키텍처 완벽 이해: 관청 시스템 비유

개별 데이터가 어떻게 포장되는지 알았으니, 이제 백성(사용자)의 민원(명령어)이 어떤 부서들을 거쳐 지하 창고(DB)까지 도달하는지 전체 흐름을 살펴보자.

데이터의 흐름은 정확히 다음과 같은 순서로 이동한다.

사용자 입력 => App => Controller => Service => Dao => DBUtil => => DB 창고

🏛️ 1. App.java (정문 안내데스크)

가장 먼저 사용자를 맞이하는 곳으로, "어떤 민원 오셨나요? 담당 부서로 안내해 드릴게요."라며 길 안내(라우팅)만 담당한다. 직접 실무를 보지는 않는다.

  • 예시: 사용자가 article write라고 입력하면, App은 "아, 게시글 작성이군요? 게시글 담당 부서(ArticleController)로 가보세요!" 하고 일을 넘긴다.

🗣️ 2. Controller (부서별 민원 접수처)

백성과 직접 대화하며 필요한 정보(제목, 내용, 아이디 등)를 물어보고 서류를 작성하는 역할을 한다.

  • 예시 (ArticleController): "글을 쓰시겠다고요? 제목은 뭘로 할까요? 내용은요?" 하고 묻는다. 대답을 다 들으면 뒷방의 실무자(Service)에게 서류를 넘긴다.
  • 예시 (MemberController): 회원가입 시 비밀번호와 비밀번호 확인을 두 번 입력받고, 두 값이 일치하는지(loginPw.equals(loginPwConfirm)) 1차적으로 꼼꼼히 검사한다.

3. Service (핵심 실무자 / 중간 관리자)

안내원(Controller)이 넘겨준 서류를 받고, "이게 법적으로(비즈니스 로직상) 문제가 없나?"를 꼼꼼히 판단한 뒤 창고지기(Dao)에게 최종 지시를 내린다.

  • 왜 굳이 Service가 필요할까?
    처음에는 그저 Dao에게 일을 전달만 하는 껍데기처럼 보일 수 있다. 하지만 기획이 복잡해지면 진가를 발휘한다.
    • 예시 1: MemberService는 회원이 가입하려 할 때, Dao에게 "이 아이디(loginId)가 창고에 이미 있는지 확인해 줘!"라고 시켜 중복 가입을 막는다.
    • 예시 2: ArticleService는 글쓰기 요청이 들어왔을 때, 내용에 '욕설'이나 '금칙어'가 포함되어 있는지 검사하고, 문제가 있으면 창고로 보내지 않고 반려 처리를 한다.

4. Dao (Data Access Object / 지하 창고지기)

지하 창고(DB) 문을 열고 직접 물건을 넣고 빼는 현장 출동 요원이다. 오직 DB와의 통신(SQL 작성 및 실행)에만 집중한다.

  • 예시: ArticleDaoSELECT * FROM article이라는 주문서(SQL)를 작성하여 DB 창고로 내려가 데이터를 가져온다.

5. Util & Exception (마법의 자동화 도구들)

DB와 통신하려면 매번 전화선을 연결하고, 끊고, 에러를 잡는(try-catch-finally) 노가다가 필요하다. 이 반복 작업을 없애기 위해 도입된 마법의 도구들이다.

  • DBUtil (운반 로봇): 귀찮은 연결/해제 과정과 ResultSet 변환 작업을 혼자 다 해주는 똑똑한 로봇이다. Dao가 명령만 내리면 결과를 쏙 뽑아다 준다.
  • SecSql (보안 양식지): 해킹(SQL Injection)을 막기 위해, title = ?처럼 빈칸을 뚫어놓고 나중에 데이터를 안전하게 채워 넣게 해주는 자동화 서식이다.

정리

처음에는 App.java 하나에 모든 코드를 때려 넣는 것이 편해 보일 수 있다. 하지만 이렇게 건물(클래스)의 역할을 명확히 나누어 놓는 MVC(Model-View-Controller) 패턴을 적용하면 엄청난 마법이 일어난다.

만약 서비스가 커져서 "게시글 작성 시 작성자에게 포인트 10점을 지급하라!"는 새로운 규칙이 생겼다고 가정해 보자.
과거의 통짜 코드였다면 수백 줄의 코드를 뒤져가며 수정해야 했겠지만, 이제는 App이나 Dao는 건드릴 필요 없이 오직 ArticleService만 열어서 포인트 지급 로직을 딱 한 줄 추가하면 끝난다.

이처럼 '각자의 역할에만 충실하게 책임을 분리하는 것', 이것이 바로 좋은 소프트웨어 아키텍처의 핵심이자 백엔드 개발자로 나아가는 가장 중요한 첫걸음이다.

비전공자로서 이 수많은 파일들을 하나하나 타이핑하고 에러를 잡아가며 여기까지 오신 것, 정말 진심으로 박수를 보내고 싶어요!

지금 파일이 너무 많아서 머릿속이 복잡한 게 당연해요. 앞서 말씀드린 '관청(국가 시스템) 비유'를 그대로 가져와서, 우리가 만든 파일들이 각각 어떤 역할을 하고 코드 안에서 무슨 일을 하는지 아주 쉽고 자세하게, 전체 지도를 그려드릴게요!


[전체 조감도] 데이터는 어떻게 흘러갈까?

백성(사용자)이 명령어를 입력하면, 데이터는 다음 순서대로 관청을 통과해서 지하 창고(DB)로 들어갑니다.

👤 사용자 입력 ➡️ App (정문 안내) ➡️ Controller (민원 접수) ➡️ Service (실무 검토) ➡️ Dao (창고지기) ➡️ DBUtil (운반 로봇) ➡️ 🗄️ DB (지하 창고)

이제 각 건물의 문을 열고 들어가서 코드를 살펴볼게요!


🏛️ 1. App.java (정문 안내데스크)

가장 먼저 사용자를 맞이하는 곳이에요. "어떤 민원 오셨나요? 담당 부서로 안내해 드릴게요." 하는 역할만 합니다.

  • run() 메서드:
    • Scanner sc = new Scanner(System.in); : 백성의 목소리(키보드 입력)를 듣는 마이크를 켭니다.
    • Connection conn = DriverManager.getConnection(...) : 지하 창고(DB)와 연결되는 전용 인터폰(전화선)을 연결합니다.
  • action() 메서드 (안내원 역할):
    • MemberController memberController = new MemberController(sc, conn); : 회원 담당 안내원을 부릅니다. 이때 마이크(sc)와 인터폰(conn)을 건네줍니다. (너희가 직접 일해!)
    • if (cmd.equals("article write")) { articleController.doWrite(); } : 사용자가 "게시글 쓸래"라고 하면, 게시글 담당 안내원에게 "야, 네가 처리해!" 하고 일을 넘깁니다.

🗣️ 2. Controller (부서별 민원 접수처)

백성과 직접 대화하며 필요한 정보(제목, 내용, 아이디 등)를 물어보고 받아적는 역할을 합니다.
(ArticleController, MemberController가 여기에 속해요)

ArticleController.java (게시글 부서)

  • doWrite() (글쓰기 접수):
    • System.out.print("제목 : "); String title = sc.nextLine(); : 사용자에게 제목과 내용을 물어보고 받아 적습니다.
    • int id = articleService.doWrite(title, body); : 다 적었으면 뒷방에 있는 실무자(Service)에게 "이 제목이랑 내용으로 글 좀 써줘!" 하고 서류를 넘깁니다.

MemberController.java (회원 부서)

  • doJoin() (회원가입 접수):
    • 비밀번호와 비밀번호 확인을 두 번 물어보고 일치하는지(loginPw.equals(loginPwConfirm)) 꼼꼼하게 검사합니다.
    • memberService.isLoginIdDup(...) : 사용자가 적어낸 아이디가 중복인지 실무자에게 확인해 달라고 요청합니다.

3. Service (핵심 실무자 / 중간 관리자)

안내원(Controller)이 넘겨준 서류를 받고, "이게 법적으로 문제가 없나?" 판단한 뒤 창고지기(Dao)에게 지시를 내립니다.
(ArticleService, MemberService)

  • 왜 굳이 Service가 필요할까?
    지금 당장 코드를 보면 return articleDao.doWrite(title, body); 처럼 그냥 창고지기한테 토스만 하는 것 같죠? 하지만 나중에 "글을 쓸 때 욕설이 있으면 막아라!", "회원가입 하면 포인트 100점을 줘라!" 같은 복잡한 '규칙(비즈니스 로직)'이 생기면 무조건 여기서 검사해야 해요. 그래서 미리 자리를 만들어 둔 거랍니다.

4. Dao (Data Access Object / 지하 창고지기)

지하 창고(DB) 문을 열고 직접 물건을 넣고 빼는 현장 출동 요원입니다.
(ArticleDao, MemberDao)

ArticleDao.java

  • getArticles() (글 목록 가져오기):
    • SecSql sql = new SecSql(); : 안전한 마법의 양식지(SecSql)를 꺼냅니다.
    • sql.append("SELECT * FROM article"); : 양식지에 창고에서 물건을 다 빼오라는 명령어(SQL)를 적습니다.
    • DBUtil.selectRows(conn, sql); : 운반 로봇(DBUtil)에게 전화선(conn)과 양식지(sql)를 쥐여주며 "창고 가서 물건 다 쓸어와!" 하고 심부름을 시킵니다.

5. Util & Exception (마법의 자동화 도구들)

이 파일들은 우리가 반복되는 노가다(복붙)를 피하기 위해 만든 특별한 도구 상자예요.

  • SecSql.java (보안 양식지): 사용자가 제목에 해킹 암호를 넣을까 봐, title = ? 처럼 물음표로 빈칸을 뚫어놓고 나중에 안전하게 데이터를 채워 넣는 도구예요.
  • DBUtil.java (운반 로봇): 아까 선생님이 말한 복잡한 try-catch-finally (전화 걸고, 끊고, 에러 잡는 일)를 혼자서 다 해주는 똑똑한 로봇이에요. Dao가 얘한테 일만 시키면 결과를 쏙 뽑아다 줍니다.
  • SQLErrorException.java (전용 비상벨): 창고 로봇(DBUtil)이 일하다가 에러가 나면, 복잡한 자바 에러 대신 우리가 만든 깔끔한 비상벨을 울려서 어디서 문제가 터졌는지 쉽게 알 수 있게 해줘요.

6. Model / DTO (규격 포장 상자)

Article.java

  • 지하 창고에서 가져온 데이터는 원래 거칠고 모양이 제각각이에요(Map 보따리 형태).
  • 이 파일은 그 보따리에서 데이터를 꺼내서 id, title, body라는 예쁜 칸막이가 있는 규격 상자로 옮겨 담는 역할을 해요. 그래야 자바 프로그램 안에서 안전하고 깔끔하게 들고 다닐 수 있거든요!

7. Test 패키지 (연습장)

  • JDBCConnectTest.java 등: 본 프로그램(App)에 코드를 합치기 전에, "창고 문이 잘 열리나?", "저장이 잘 되나?" 하고 따로 빼서 안전하게 연습해 본 실험실이에요. 본 프로그램의 흐름에는 영향을 주지 않아요.

profile
성심당마스코트

0개의 댓글