묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
사용자가 캘린더를 보유했는지 여부로 확인할 것 같습니다. 이유는 추후 서비스 확장할 때 조금 더 수월할 것 같다는 생각이 들었습니다.프로젝트를 시작하면서부터 아래와 같은 생각을 했습니다.- "누구나 호스트가 되면 안될까?" - "호스트 역시도 커피챗을 신청할 수 있게 하고 싶다." 그래서 위와 같이 서비스 정책을 확장할 때를 대비해서 (마이그레이션 비용을 줄이는 방향으로) is_host로 검사하는 것이 아닌 일단 캘린더 보유 여부로 호스트인지 아닌지 확인하자라고 판단했습니다.
-
해결됨원클릭으로 AI가 생성해주는 Youtube 쇼츠 만들기 자동화(with n8n)
Img 생성 시 오류
다른 질문에도 있는 것 같은데, 이미지 생성 시 자꾸 이런식으로 새로운 cell 을 만들어냅니다. 해결 방법이 있을까요?
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
두 요소를 모두 사용 하나, 각각 다른 용도로 쓸 것입니다. is_host 용도: 호스트 전용 엔드포인트 접근 통제 및 UI 메뉴 노출, 권한 기반 라우팅 호스트를 위한 endpoint API 에 대한 이용 허가 여부를 결정하는 호스트의 access token 생성 및 엔드포인트 함수 내의 조건문을 위해 필요한 모델 필드라고 생각합니다. 예상되는 우려점은 호스트 유저가 더이상 host가 되지 않을 때, host를 위한 API의 이용 허가를 거부 해야하기 때문에 is_host 필드값의 확실한 업데이트가 필요합니다. 사용자 캘린더 보유 여부 용도: 실제 예약 생성/동기화가 필요한 시점에 필수 체크, 호스트의 온보딩 플로우의 완료조건 게스트의 성공적인 Booking 을 위해서는 실제 캘린더에 대한 접근이 필요하기 때문에, 호스트는 게스트가 Booking 하기 위해서는 캘린더를 무조건 보유해야 합니다. 예상되는 우려점은 호스트의 캘린더는 호스트가 Booking을 받을 준비가 되있다면 항상 존재 해야하며, 실제 올바른 호스트에 매핑이 되어야합니다. 만약 캘린더가 존재 하지않거나, 호스트 캘린더 ID 에 대한 검증이 제대로 동작 하지 않는다면, 게스트의 Booking 이 실패로 이어지거나 다른 호스트 캘린더에 등록이 될 수 있습니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 3회차 과제
중복 예약을 데이터베이스의 고유값 제약으로 예방하는 방법조건은 1)동일 일자와 동일 타임 슬롯인 경우 중복으로 간주 2)약속(부킹)이 취소되어 attendance_status 모델 필드값이 cancellled인 경우 예약 가능 위를 구현하기 위해 데이터베이스에게 아래 정보가 고유해야 한다는 특별한 규칙을 알려줄 수 있습니다.1)누가 예약하는지2)언제 예약하는지3)어느 시간대에 예약하는지 위의 세가지가 모두 같으면 중복 예약이라고 판단 합니다. 취소된 재예약은 가능하게 하기 위해고유성 규칙에 한가지 예외를 부여합니다.호스트, 날짜, 시간 슬롯의 조합은 고유해야 한다. 예외적으로 예약상태가 "취소됨"이라면 그 예약은 위의 고유성 규칙을 무시해 달라고 합니다. 새로운 예약 생성 시:데이터베이스는 예약하려는 호스트, 날짜, 시간슬롯 정보를 보고, 현재 '취소됨' 상태가 아닌 다른 예약이 이미 있는지 찾아봅니다.만약 '취소됨' 상태가 아닌 다른 예약이 이미 같은 자리에 있다면, 데이터베이스는 "안돼! 이미 예약된 자리야!"라고 하며 새로운 예약을 저장하지 못하게 합니다.만약 같은 자리에 '취소됨' 상태가 아닌 예약이 없다면, 데이터베이스는 새로운 예약을 성공적으로 저장합니다.예약 취소 시:어떤 예약이 '취소됨(cancelled)' 상태로 변경되면, 데이터베이스의 이 "특별한 규칙"은 그 취소된 예약을 더 이상 고유성 검사 대상에서 제외합니다.이제 그 자리는 "비어있는" 것처럼 간주되므로, 다른 사람이 같은 호스트, 날짜, 시간슬롯 정로 새로운 예약을 만들 수 있게 됩니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
사용자가 호스트인지 두 가지 방법으로 확인 가능합니다. 1. User 모델에 선언한 is_host 모델 필드가 True인지 확인 2.사용자가 캘린더를 보유했는지 확인 이 중 두 가지 방법 모두를 사용해야 한다고 생각합니다.1. User 모델에 선언한 is_host 모델 필드가 True인지 확인 -> 관점적 측면에서 명시적이고 관리적인 역할 및 자격을 부여합니다. 즉 사용자가 호스트로 자격이 있다는 것을 의미합니다. 관리자가 특정 사용자를 호스트로 지정하거나 해제하는데 사용할 수 있고 캘린더의 유무와 별개로 관리가 가능하게 서비스의 효율을 향상시켜 줍니다. 2.사용자가 캘린더를 보유했는지 확인->실제 예약 요청이 들어 왔을 때 해당 호스트가 유효한 캘린더를 보유하고 있는지 확인하는 최종적인 역할을 할 수 있습니다. 즉 위의 두 정보가 모두 필요한 이유는 존재하지 않는 사용자의 username으로 캘린더 정보를 가져오려고 하거나 호스트가 아닌 사용자의 username으로 캘린더 정보를 가져오려고 하는 시도를 막을 수 있을것으로 예상됩니다. 요약하면 is host는 "호스트로서의 신분"을, 캘린더 보유 여부는 "호스트로서의 활동을 나타내는 정보여서 모두 필요합니다.
-
미해결Airflow 마스터 클래스
github에 회원가입이 안되는데 원인이 뭔지 모르겠어요 ㅠ
캡차 응답을 확인할 수 없습니다. 문제 해결 정보는 https://docs.github.com/articles/troubleshooting-connectivity-problems/#troubleshooting-the-captcha 를 참조하세요.위와 같이 메시지가 뜨는데 회사나 기관의 네트워크을 사용하라고 하는데 꼭 고정IP를 사용해야 하는 건가 해서요. 집이나 도서관 등 유동IP는 사용이 불가능한 건가요?
-
해결됨원클릭으로 AI가 생성해주는 Youtube 쇼츠 만들기 자동화(with n8n)
FAL AI 이미지 생성요청 시 이미지가 너무 많이생성됩니다.
제목 그대로 쇼츠에 올릴 영상 5개를 만들기 위해서이미지 생성을 하면 이미지가 너무 많이생성이 됩니다. 처음에는 제가 잘못했나 싶었는데 2번째 생성시도했을때는 이미지가 122개가 생성이되네요..( REQUEST로 확인했을때 정확하게 세보지는 않았지만 중복된 이미지가 많았습니다)어디가 문제일까요 ..?(이미지 마다 눌러서 URL 복사해서 유튜브 자동화 시트에 붙여넣기 하면 영상생성은 잘됩니다)
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 3회차 과제
데이터베이스 고윳값 제약으로 예방하는 구현에 대해서 조사했습니다. 동일 일자와 동일 타임슬롯인 경우 중복Booking에 unique index를 생성하는 것인데 동일 일자 (when)과 동일 타임슬롯(time_slot_id) 조건을 생성하면 됩니다. CREATE UNIQUE INDEX uq_booking_slot ON bookings (when, time_slot_id);class Booking(SQLModel, table=True): __tablename__ = "bookings" __table_args__ = ( UniqueConstraint("when", "time_slot_id", name="uq_booking_slot"), ) attendance_status 필드값이 cancelled인 경우PostgreSQL의 경우 partial unique index가 가능합니다. where를 사용해서 field가 cancelled가 아닌 경우에 unique 제약을 걸면 됩니다. CREATE UNIQUE INDEX uq_booking_slot ON bookings (when, time_slot_id) WHERE attendance_status <> 'cancelled' ;class Booking(SQLModel, table=True): __tablename__ = "bookings" __table_args__ = ( UniqueConstraint("when", "time_slot_id", name="uq_booking_slot"), postgresql_where=text("attendance_status <> 'cancelled'") ) 다중 컬럼에 대한 unique는 종종 사용하는 방법이었는데 where를 이용해서 조건을 설정할 수 있는 건 이번에 첨 알았습니다. 필요하면 python에서 처리하곤 했었는데 데이터베이스 기능으로 이용하는게 레이스 컨디션도 피할 수 있고 장점이 있어보이네요. 실무에 적용할때도 꼭 이용해보겠습니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 5회차 과제
파일 삭제 정책booking을 삭제할때 해당 booking과 업로드된 파일을 같이 삭제하는 정책의미없는 파일이 남아있는 것은 저장 공간 낭비유출 위험을 줄여준다(?) (예: 삭제된 booking의 파일을 남겨두어 총 100개의 파일이 저장된 상태라고 가정. 그 중에 70개는 필요한 파일, 나머지 30개는 필요없는(삭제된 booking) 파일임. 파일을 바로 제거하지 않아서 100개가 다 유출되는 것보다 의미없는 파일을 바로바로 제거하여 70개만 유출되는 것이 더(?) 낫다..)그러나 한편으로는 트래픽이 적은 시간에 삭제된 booking의 파일을 주기적으로 정리하는 로직을 실행하는 것이 더 나을까 하는 생각도 드네요
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
is_host / 캘린더 보유 여부 중에 어느 것을 사용할까? 둘 다?is_host 는 반드시 사용할 것 같음is_host 필드가 있으면 캘린더 라는 다른 엔티티에 의존하지 않고 is_host 만으로 호스트 여부를 판단할 수 있어서캘린더 보유 여부는 선택적일 것 같음아래와 같은 정책을 설정해야할 필요가 있을때is_host 인데 캘린더가 있는 경우s_host 인데 캘린더가 없는 경우 아니면 '호스트는 최소 하나 이상의 캘린더는 있어야 한다' 같은 정책
-
해결됨남박사의 파이썬으로 실전 웹사이트 만들기
DB 검색하면 데이터가 없습니다. 라고 나와요. 5시간을 찾아봐도 모르겠어서 문의드려봅니다.
안녕하세요저의 환경은윈도우 11이고 파이선 3.12 버전입니다. 몽고DB는 8.2.3 x64입니다.ms 엣지 브라우저 버전 143.0.3650.96 (공식 빌드) (64비트) 입니다. 구글에서 긁어오는게 문제가 있어서네이버로 변경하여 긁어왔습니다.DB에 35개의 데이터를 넣었습니다.Studio 3T 프로그램으로 검새하면db.getCollection("board").find({$or:[// {title: { $regex: "이유", $options: "i" }},{name:{ $regex: "테스터", $options: "i" }}]})위의 쿼리로 검색을 했을 때name = '테스터'로 하면 35개 전부 나오고title = '이유'로 검색하면 3건이 나옵니다. 그런데 웹에서 list.html 검색에서는 0건으로 데이터가 없다고 나옵니다.원인을 아무리 찾아도 모르겠어서 문의드려봅니다. 위와 같이 네이버에서 검색하여 DB에 넣음title 이유로 검색하면 3건name 테스터로 검색하면 35건 조회됩니다.위와 같이 제목, '이유'로 검색하면 3건이 나와야 하는데위와 같이 데이터가 없습니다. 라고 나옵니다. # 검색 대상이 한개라도 존재할 경우 query 변수에 $or 리스트를 쿼리 합니다. if len(search_list) > 0: query = {"$or": search_list} print(query) # datas_count = board.count_documents({}) # 정확한 카운트 방식 datas_count = board.count_documents(query) print(datas_count) 위와 같이 print로 값을 보면{'$or': [{'title': {'regex': '이유'}}]}0127.0.0.1 - - [11/Jan/2026 17:42:01] "GET /list?search=0&keyword=이유 HTTP/1.1" 200 -이렇게 나옵니다.쿼리는 title 이유라고 잘 들어가는 것 같은데검색 결과가 0건으로 나옵니다.DB에서 검색이 제대로 안되는걸로 보이는데 어느부분일지 찾지 못하고 있습니다. 아래는 전체 소스코드 입니다. < run.py 전체 코드 > from flask import Flask from flask import request from flask import render_template from flask_pymongo import PyMongo from datetime import datetime from bson.objectid import ObjectId from flask import abort from flask import redirect from flask import url_for import time import math app = Flask(__name__) app.config["MONGO_URI"] = "mongodb://localhost:27017/myweb" mongo = PyMongo(app) @app.template_filter("formatdatetime") def format_datetime(value): if value is None: return "" now_timestamp = time.time() offset = datetime.fromtimestamp(now_timestamp) - datetime.utcfromtimestamp(now_timestamp) value = datetime.fromtimestamp((int(value) / 1000)) + offset return value.strftime('%Y-%m-%d %H:%M:%S') @app.route("/list") def lists(): page = request.args.get("page", default=1, type=int) limit = request.args.get("limit", 5, type=int) search = request.args.get("search", -1, type=int) keyword = request.args.get("keyword", type=str) query = {} search_list = [] if search == 0: search_list.append({"title": {"regex": keyword}}) elif search == 1: search_list.append({"contents": {"regex": keyword}}) elif search == 2: search_list.append({"title": {"regex": keyword}}) search_list.append({"contents": {"regex": keyword}}) elif search == 3: search_list.append({"name": {"regex": keyword}}) if len(search_list) > 0: query = {"$or": search_list} print(query) board = mongo.db.board datas = board.find(query).skip((page - 1) * limit).limit(limit) datas_count = board.count_documents(query) print(datas_count) tot_count = datas_count last_page_num = math.ceil(tot_count / limit) block_size = 5 block_num = int((page - 1) / block_size) block_start = int((block_size * block_num) + 1) block_last = math.ceil(block_start + (block_size - 1)) return render_template( "list.html", datas=datas, datas_count=datas_count, limit=limit, page=page, block_start=block_start, block_last=block_last, last_page_num=last_page_num, search=search, keyword=keyword) @app.route("/view/<idx>") def board_view(idx): if idx is not None: page = request.args.get("page") search = request.args.get("search") keyword = request.args.get("keyword") board = mongo.db.board data = board.find_one({"_id": ObjectId(idx)}) if data is not None: result = { "id": data.get("_id"), "name": data.get("name"), "title": data.get("title"), "contents": data.get("contents"), "pubdate": data.get("pubdate"), "view": data.get("view") } return render_template("view.html", result=result, page=page, search=search, keyword=keyword) return abort(404) @app.route("/write", methods=["GET", "POST"]) def board_write(): if request.method == "POST": name = request.form.get("name") title = request.form.get("title") contents = request.form.get("contents") print(name, title, contents) current_utc_time = round(datetime.utcnow().timestamp() * 1000) board = mongo.db.board post = { "name": name, "title": title, "contents": contents, "pubdate": current_utc_time, "view": 0 } x = board.insert_one(post) print(x.inserted_id) return redirect(url_for("board_view", idx=x.inserted_id)) else: return render_template("write.html") if __name__ == "__main__": app.run(debug=True, port=9000) < list.html 전체 코드 ><script> function search() { var v_search = document.getElementById("search").value; var v_keyword = document.getElementById("keyword").value; if(v_search == "" || v_keyword == "") { return false; } else { self.location.href = "{{url_for('lists')}}?search=" + v_search + "&keyword=" + v_keyword; } } </script> <!-- % if datas_count() > 0 %} # Deprecated 됨--> {% if datas_count > 0 %} <table> <thead> <tr> <td>번호</td> <td>제목</td> <td>이름</td> <td>날짜</td> <td>조회수</td> </tr> </thead> <tbody> <!-- 반복되는 구간 --> {% for data in datas %} <tr> <td>{{loop.index + ((page - 1) * limit)}}</td> <td><a href="{{url_for('board_view', idx=data._id, page=page, search=search, keyword=keyword)}}">{{data.title}}</a></td> <td>{{data.name}}</td> <td>{{data.pubdate | formatdatetime}}</td> <td>{{data.view}}</td> </tr> {% endfor %} <!-- 반복되는 구간 끝 --> </tbody> </table> {% if block_start - 1 > 0 %} <a href="{{url_for('lists', page=block_start - 1, search=search, keyword=keyword)}}">[이전]</a> {% endif %} {% for i in range(block_start, block_last + 1) %} {% if i > last_page_num %} {{ i }} {% else %} {% if i == page %} <b>{{ i }}</b> {% else %} <a href="{{url_for('lists', page=i, search=search, keyword=keyword)}}">{{ i }}</a> {% endif %} {% endif %} {% endfor %} {% if block_last < last_page_num %} <a href="{{url_for('lists', page=block_last + 1, search=search, keyword=keyword)}}">[다음]</a> {% endif %} <select name="search" id="search"> <option value="" {% if search == '' or search == -1 %} selected {% endif %}>검색대상</option> <option value="0" {% if search == 0 %} selected {% endif %}>제목</option> <option value="1" {% if search == 1 %} selected {% endif %}>내용</option> <option value="2" {% if search == 2 %} selected {% endif %}>제목+내용</option> <option value="3" {% if search == 3 %} selected {% endif %}>작성자</option> </select> <input type="text" name="keyword" id="keyword" {% if keyword != "" %} value={{keyword}} {% endif %}> <input type="button" value="검색" onclick="search()"> {% else %} <h3>데이터가 없습니다.</h3> {% endif %} <a href="{{url_for('board_write')}}">글작성</a>오타가 있는지 문제가 될만한 부분이 있는지 5시간이나 찾아봤는데 도저히 못찾아서 문의드려봅니다.봐주셔서 감사합니다.버전 143.0.3650.96 (공식 빌드) (64비트)버전 143.0.3650.96 (공식 빌드) (64비트)ㅍ
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제
기획의 관점에서 Host 체크 방법is_host 필드 체크사용자가 호스트인지 먼저 확인을 하게 되므로 호스트 등록, 호스트만 조회, 호스트에 주어진 권한 제어 등 호스트를 따로 관리하고 싶을때 장점이 있습니다캘린더 보유 체크캘린더로 호스트일 경우는 호스트, 게스트 구분이 회원 등급이나 종류가 아닌 모두가 호스트나 게스트가 될수 있고 예약이 서로에게 일어날 수 있을때 장점이 있습니다. 구글 캘린더로 예를 들면 모두가 초대가 가능하고 초대도 가능한 방식입니다. 내가 선택한다면저라면 기본적으로는 is_host 필드로 체크하고 실제 예약을 받는다면 캘린더 체크를 이후에 하겠습니다. 호스트 관리를 따로 하게 되면 호스트만 따로 조회도 가능하고 호스트를 하고 싶지 않을때는 호스트가 되지 않는 것도 가능합니다. 또한 is_host를 기본 true로 하게 되면 모두가 호스트가 되는 방식도 가능하므로 앞에서 가정한 모두가 호스트/게스트인 방식도 쉽게 전환이 가능합니다. 예약 가능한 캘린더 체크는 실제로 이사람이 호스트라고 하더라도 예약일자가 있는지에 따라 예약화면 컨트롤 등이 가능하기 때문에 체크가 필요합니다. 요약하면 is_host 체크를 할 때 상황에 따라 유연하게 호스트 <-> 게스트 전환이 쉽고 필요한 경우 호스트이면서 게스트인 정책도 가능하기 때문에 좀 더 유연한 방법이라고 생각합니다.
-
미해결프로그래밍 시작하기 : 파이썬 입문 (Inflearn Original)
맥 os vscode 설정 - python interpreter select
안녕하세요.맥 os에 vscode를 설정하려고 하는데 python interpreter select -> 알맞는 python 버전을 클릭해도 선생님과 같은 파란색 바가 안 뜨는데 어떤 문제가 있을까요? 코드를 입력해도 그에 따른 설명이 선생님과 같이 안 뜹니다(ex. 명령어 print를 입력해도 설명이 안 뜸 )
-
해결됨원클릭으로 AI가 생성해주는 Youtube 쇼츠 만들기 자동화(with n8n)
fal.ai 요청시 wait 보다는 status 체크가 더욱 좋을것 같아요
fal.ai 요청시 wait 무작정 넉넉히 잡는건 서버 메시지큐 상태를 알수없고 내 TASK가 언제 처리될지 모르는 상태에서 감(?) 으로 때리는 방법이라 엣지 케이스가 너무 많은것 같아요주기적으로 Status 호출해서 체크하는 방법이 좋아보입니다. falai POST API 생성후 응답이 다음과 같이 오는데 status_url 을 사용하면 간단히 처리가 가능할것 같습니다.{ "status": "COMPLETED", "request_id": "728614ce-f8c3-498c-8174-e4beea63b48d", "response_url": "https://queue.fal.run/fal-ai/kling-video/requests/728614ce-f8c3-498c-8174-e4beea63b48d", "status_url": "https://queue.fal.run/fal-ai/kling-video/requests/728614ce-f8c3-498c-8174-e4beea63b48d/status", "cancel_url": "https://queue.fal.run/fal-ai/kling-video/requests/728614ce-f8c3-498c-8174-e4beea63b48d/cancel", "logs": null, "metrics": { "inference_time": 41.99311113357544 } }status_url 을 다시 http request로 던지고응답문에 switch 걸고 WAIT 상태에서 loop 돌려주면 깔끔할것 같아요 while 문처럼 내부에 sleep 있는 것 처럼 loop 돌리는게 호율적이여 보입니다.코드 리뷰 정도의 의견이라고 생각해주세요!강의 잘보고 있습니다. 감사합니다. 예시>추가적으로 위에 상태조회 방법과 함께 MERGE 처리도 코드 필요없이 이렇게 구성하면 별도 code node 없이 아래처럼 가능할것 같아요! 참고 부탁드립니다. 추가1Http auth 부분도 Header Auth 로 별도로 지정해주고 아래처럼 호출하는게 보안상 안전해 보입니다! 추가2falai 외에도 Creatomate render post 요청 이후 응답에서 ID 조합으로 상태 체크 WAIT 를 구성하는것도 괜찮을 것 같아요!API DOCShttps://creatomate.com/docs/api/reference/get-the-status-of-a-rendercurl -X GET https://api.creatomate.com/v2/renders/RENDER_ID \ -H "Authorization: Bearer YOUR_API_KEY_HERE" Response: { "id": "a862048b-d0dc-4029-a4ef-e172e8ded827", "status": "succeeded", "url": "https://cdn.creatomate.com/renders/a862048b-d0dc-4029-a4ef-e172e8ded827.mp4", "snapshot_url": "https://cdn.creatomate.com/snapshots/a862048b-d0dc-4029-a4ef-e172e8ded827.jpg", "output_format": "mp4", "render_scale": 1, "width": 1280, "height": 720, "frame_rate": 60, "duration": 3, "file_size": 10804 }
-
미해결[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 4회차 과제 제출
쟁점 정리예약 일정 변경과 관련해서 결정해야 할 핵심은 다음과 같습니다:일자/타임슬롯 변경을 허용할 것인가? vs 취소 후 재예약만 가능하게 할 것인가?변경을 허용한다면, 언제까지 허용할 것인가?당일 변경은 불가능하다 (이건 확정)내가 선택한 정책일자/타임슬롯 변경을 허용하되, 예약일 24시간 전까지만 가능근거:"취소 → 재예약" 방식은 사용자가 불편하고, 원하는 시간이 이미 찼을 수도 있음24시간 전까지는 게스트에게 충분한 유연성을 주면서도, 호스트가 하루 전부터 안정적으로 준비할 수 있음규칙이 단순해서 사용자가 이해하기 쉽고, 구현도 간단함구현 시나리오시나리오 1: 여유 있는 변경 (성공)민수는 2월 10일 오후 2시를 예약했습니다.2월 7일에 2월 11일 오후 4시로 바꾸고 싶어졌습니다.→ 변경 가능 (24시간 이상 여유)→ "변경이 완료되었습니다."시나리오 2: 임박한 변경 시도 (실패)지영은 2월 10일 오후 2시를 예약했습니다.2월 9일 오후 3시에 시간을 바꾸려고 합니다.→ 변경 불가 (24시간 미만)→ "예약일 24시간 전까지만 변경 가능합니다. 취소 후 재예약해주세요."시나리오 3: 당일 변경 시도 (실패)현우는 2월 10일 오후 2시를 예약했습니다.2월 10일 오전 11시에 오후 4시로 바꾸려고 합니다.→ 변경 불가 (당일)→ "당일 일정 변경은 불가능합니다."이상입니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 1회차 과제 제출합니다.
저는 is_host 필드와 calendar 존재 여부를 둘 다 검증해야 한다고 판단했습니다.왜 두 가지를 모두 사용해야 할까요?우선 두 요소는 서로 다른 의미를 가지고 있다고 생각합니다.is_host: 이 사용자가 호스트 자격이 있는가? (역할)calendar 존재: 이 호스트가 실제로 운영 중인가? (상태)현실로 비유하자면, is_host는 사업자등록증을 가지고 있는 상태이고, calendar가 있다는 건 실제로 가게를 오픈한 상태라고 볼 수 있습니다. 사업자등록증은 있지만 아직 가게를 열지 않은 사람도 있을 수 있잖아요?그래서 둘 중 하나만 체크하면 문제가 생긴다고 봅니다.만약 calendar 존재 여부만 체크한다면?calendar만 보고 판단하면 권한 체계가 무너질 수 있습니다. 예를 들어 일반 사용자가 억지로 캘린더를 생성하면 호스트로 둔갑할 수 있는 보안 문제가 생기죠. 또 호스트가 일시적으로 캘린더를 삭제했다가 다시 만들 때마다 권한 관리가 복잡해집니다.만약 is_host만 체크한다면?반대로 is_host만 체크하면, 캘린더가 없는데도 예약 관련 API를 호출할 수 있게 되어서 500 에러가 발생할 수 있습니다. "호스트인데 캘린더는 없는" 이상한 상태를 어떻게 처리해야 할지도 애매해지고요.두 가지를 순차적으로 검증하는 게 좋습니다저는 이렇게 단계적으로 확인하는 게 맞다고 봅니다:먼저 is_host로 호스트 자격이 있는지 확인그 다음 calendar 존재로 실제 운영 가능한지 확인이렇게 하면 사용자에게도 명확한 피드백을 줄 수 있어요. "호스트 권한이 없습니다" vs "캘린더를 먼저 생성해주세요" 처럼 무엇을 해야 하는지 정확히 알려줄 수 있죠.미래 확장성도 고려했습니다나중에 서비스가 커지면 호스트 상태를 더 세분화할 수도 있을 것 같아요:준비 중 호스트 (is_host=true, 캘린더 없음) → 대시보드 접근만 가능운영 중 호스트 (is_host=true, 캘린더 있음) → 모든 기능 사용 가능휴면 호스트 (is_host=true, 캘린더 비활성화) → 기존 예약 조회만 가능이런 식으로 관리하려면 역할과 리소스를 분리해서 체크하는 게 필수라고 생각합니다.성능 걱정은 없을까요?is_host는 User 모델의 필드라서 추가 DB 쿼리가 필요 없고, calendar 조회도 어차피 해야 하는 작업이라 성능에 큰 영향은 없다고 봅니다.
-
해결됨38군데 합격 비법, 2025 코딩테스트 필수 알고리즘
Linked List Element Delete Explanation Problem
1. 현재 학습 진도몇 챕터/몇 강을 수강 중이신가요?어떤 알고리즘을 학습하고 계신가요?여기까지 이해하신 내용은 무엇인가요? 2. 어려움을 겪는 부분어느 부분에서 막히셨나요?코드의 어떤 로직이 이해가 안 되시나요?어떤 개념이 헷갈리시나요? 3. 시도해보신 내용문제 해결을 위해 어떤 시도를 해보셨나요?에러가 발생했다면 어떤 에러인가요?현재 작성하신 코드를 공유해주세요 이렇게 구체적으로 알려주시면, 더 정확하고 도움이 되는 답변을 드릴 수 있습니다! 😊 아래 코드(TODO; 제가 만든 삭제코드, 정답지; 제공된 교재 답 풀이본) 에 대해 문의드립니다. 5,9,12,27 일때 정답지를 통해 결과를 뽑아보면 삭제가 안되는 현상이 일어납니다. 이와 관련해 어떻게 생각하실지 여쭙습니다!class Node: def __init__(self, data): self.data = data self.next = None class LinkedList: def __init__(self, value): self.head = Node(value) def append(self, value): cur = self.head while cur.next is not None: cur = cur.next cur.next = Node(value) def print_all(self): cur = self.head while cur is not None: print(cur.data) cur = cur.next def get_node(self, index): node = self.head count = 0 while count < index: node = node.next count += 1 return node def add_node(self, index, value): new_node = Node(value) if index == 0: new_node.next = self.head self.head = new_node return node = self.get_node(index - 1) next_node = node.next node.next = new_node new_node.next = next_node # def delete_node(self, index): # TODO # if index == 0: # 첫번째 노드를 불러와야 출력이 가능하므로 0번째 인덱스를 제거하려고하면 1번쨰 인덱스를 head로 지정 # self.head = self.head.next # return # before_node = self.get_node(index-1) # 1. 제거해야할 노드 이전 노드 찾기 # delete_node = self.get_node(index) # before_node.next = delete_node.next # 2. 이전 노드 next를 제거할 노드의 next로 # delete_node.next = None # 3. 제거할 노드의 next 를 None # return "index 번째 Node를 제거해주세요!" def delete_node(self, index): # 정답지 if index == 0: # 첫번째 노드를 불러와야 출력이 가능하므로 0번째 인덱스를 제거하려고하면 1번쨰 인덱스를 head로 지정 self.head = self.head.next return before_node = self.get_node(index-1) # 1. 제거해야할 노드 이전 노드 찾기 before_node = before_node.next.next return "index 번째 Node를 제거해주세요!" linked_list = LinkedList(5) linked_list.append(9) linked_list.append(12) linked_list.append(27) print("제거 전") linked_list.print_all() print("제거 후") linked_list.delete_node(3) linked_list.print_all()
-
해결됨원클릭으로 AI가 생성해주는 Youtube 쇼츠 만들기 자동화(with n8n)
EDIT 영상편집요청 에러
사진과 같이 Bad request - please check your parameters [item 0]400 - "{\"hint\":\"No template was found with that ID.\",\"documentation\":\"https://creatomate.com/docs/api/quick-start/introduction\"}"라고 뜹니다 뭐가 문제일까요 ??
-
미해결[퇴근후딴짓] 빅데이터 분석기사 실기 (작업형1,2,3)
수강 기한 연장 문의
안녕하세요, 강사님.강의의 수강 기한이 곧 만료되어 문의드립니다.최근 1년간 다른 직종에 관련된 일을 하느라 강의를 거의 수강하지 못했습니다.이번 연도에 강의를 들으며 빅데이터분석기사 실기를 준비하고 싶은데, 혹시 수강기한을 연장해 주실 수 없을까요?제 메일은 ksforuo@gmail.com 입니다.
-
해결됨[매일 완독 챌린지] 저자와 함께하는 <FastAPI로 기획에서 출시까지>
4주 2회차 과제
안녕하세요. 과제를 잘 못 이해한건가 싶기도 하지만 아래와 같은 조건하에 구현을 해 봤습니다. 수업 과정에서 Front-end는 고정인 상황이기 때문에 기존 API는 변경이 없다는 가정하에 변경 해 봤습니다. 시간대1개와 요일n개의 설정이 하나의 row로 묶여있는 상태로는 요일별 시간대 변동에 대응하기 까다로운 것 같아 요일별 개별 row로 저장하도록 일부 수정 하였습니다. API endpoint 모델 변경을 피하기 위해서 내부적으로 여러개의 요일별 row로 나누어 저장하도록 했습니다. 고민중에 작가님께서 올려 놓으신 레퍼런스 코드를 봤는데, 책에서 언급하신 것 처럼 postgres 경우와 sqlite 경우로 코드가 분기 되는 것을 보았고, 지금은 교육 과정이기 때문이라고 생각 하지만 배포 코드와 개발 환경 코드가 다른것은 여러모로 좋지 않은 것 같아 현재는 sqlite 기준으로 개별 DB구현에서만 지원하는 것은 배제하는 방향으로 구현 했습니다. 1과 같이 변경 함으로써, 기 등록된 정보의 수정에서는 개별 날짜 별 시간대 설정 면에서 자유도가 생겼다고 생각 됩니다.타임슬롯의 변경과 삭제에 관해서는 내부적으로는 time-slot으로 관리하지만, 외부공개 id는 아니므로, 키 로서 start-time, end-time, weekday (API형태로서는 리스트)조합으로 정의 해서 동작 하도록 구현했습니다.특히 삭제의 경우는 부득이 delete method의 경우는 payload가 포함되는것이 받아들여지지 않는 경우도 있는것을 고려해서, 새 등록 값 두가지(new_start_time과, new_end_time) 값이 모두 제공되고 같은 경우를 특정하여 삭제 동작으로 정의하여 동작하도록 구현했습니다. class TimeSlotUpdateByGroupIn(SQLModel): start_time: time end_time: time weekdays: Weekdays new_start_time: time | None = None new_end_time: time | None = None new_weekdays: Weekdays | None = None @model_validator(mode="after") def check_update_fields(self): update_fields = { "new_start_time": self.new_start_time, "new_end_time": self.new_end_time, "new_weekdays": self.new_weekdays, } if not any(value is not None for value in update_fields.values()): raise ValueError("최소 하나의 수정 필드는 반드시 제공되어야 합니다.") return self @router.post("/time-slots", status_code=status.HTTP_201_CREATED, response_model=TimeSlotOut) async def create_time_slot( user: CurrentUserDep, session: DbSessionDep, payload: TimeSlotCreateIn, ) -> TimeSlotOut: if not user.is_host: raise GuestPermissionError() weekdays = sorted(set(payload.weekdays)) # dup. check with already exist one stmt = select(TimeSlot).where( and_( TimeSlot.calendar_id == user.calendar.id, TimeSlot.weekday.in_(weekdays), TimeSlot.start_time < payload.end_time, TimeSlot.end_time > payload.start_time, ) ) result = await session.execute(stmt) existing_time_slot = result.scalars().first() if existing_time_slot: raise TimeSlotOverlapError() time_slots = [ TimeSlot( calendar_id=user.calendar.id, start_time=payload.start_time, end_time=payload.end_time, weekday=weekday, ) for weekday in weekdays ] session.add_all(time_slots) await session.commit() if not time_slots: raise TimeSlotOverlapError() return TimeSlotOut( start_time=time_slots[0].start_time, end_time=time_slots[0].end_time, weekdays=weekdays, created_at=time_slots[0].created_at, updated_at=time_slots[0].updated_at, ) @router.get( "/time-slots/{host_username}", status_code=status.HTTP_200_OK, response_model=list[TimeSlotOut], ) async def get_host_timeslots( host_username: str, session: DbSessionDep, ) -> list[TimeSlotOut]: stmt = ( select(User) .where(User.username == host_username) .where(User.is_host.is_(true())) ) result = await session.execute(stmt) host = result.scalar_one_or_none() if host is None or host.calendar is None: raise HostNotFoundError() stmt = select(TimeSlot).where(TimeSlot.calendar_id == host.calendar.id) result = await session.execute(stmt) time_slots = result.scalars().all() grouped: dict[tuple[time, time], TimeSlotOut] = {} for time_slot in time_slots: key = (time_slot.start_time, time_slot.end_time) if key not in grouped: grouped[key] = TimeSlotOut( start_time=time_slot.start_time, end_time=time_slot.end_time, weekdays=[time_slot.weekday], created_at=time_slot.created_at, updated_at=time_slot.updated_at, ) continue grouped[key].weekdays.append(time_slot.weekday) if time_slot.created_at < grouped[key].created_at: grouped[key].created_at = time_slot.created_at if time_slot.updated_at > grouped[key].updated_at: grouped[key].updated_at = time_slot.updated_at if not grouped: raise TimeSlotNotFoundError() for time_slot in grouped.values(): time_slot.weekdays = sorted(set(time_slot.weekdays)) return list(grouped.values()) @router.patch( "/time-slots", status_code=status.HTTP_200_OK, response_model=TimeSlotOut | None, ) async def update_time_slot( user: CurrentUserDep, session: DbSessionDep, payload: TimeSlotUpdateByGroupIn, ) -> TimeSlotOut | None: if not user.is_host: raise GuestPermissionError() current_weekdays = sorted(set(payload.weekdays)) stmt = select(TimeSlot).where( and_( TimeSlot.calendar_id == user.calendar.id, TimeSlot.start_time == payload.start_time, TimeSlot.end_time == payload.end_time, ) ) result = await session.execute(stmt) current_slots = result.scalars().all() if not current_slots: raise TimeSlotNotFoundError() existing_weekdays = sorted({slot.weekday for slot in current_slots}) if existing_weekdays != current_weekdays: raise TimeSlotNotFoundError() delete_only = ( payload.new_start_time is not None and payload.new_end_time is not None and payload.new_start_time == payload.new_end_time ) if delete_only: current_ids = [slot.id for slot in current_slots] await session.execute(delete(TimeSlot).where(TimeSlot.id.in_(current_ids))) await session.commit() return None new_start_time = payload.new_start_time or payload.start_time new_end_time = payload.new_end_time or payload.end_time new_weekdays = sorted(set(payload.new_weekdays or payload.weekdays)) if new_start_time >= new_end_time: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail="시작 시간은 종료 시간보다 빨라야 합니다.", ) current_ids = [slot.id for slot in current_slots] stmt = select(TimeSlot).where( and_( TimeSlot.calendar_id == user.calendar.id, TimeSlot.weekday.in_(new_weekdays), TimeSlot.start_time < new_end_time, TimeSlot.end_time > new_start_time, TimeSlot.id.not_in(current_ids), ) ) result = await session.execute(stmt) existing_time_slot = result.scalars().first() if existing_time_slot: raise TimeSlotOverlapError() await session.execute(delete(TimeSlot).where(TimeSlot.id.in_(current_ids))) new_time_slots = [ TimeSlot( calendar_id=user.calendar.id, start_time=new_start_time, end_time=new_end_time, weekday=weekday, ) for weekday in new_weekdays ] session.add_all(new_time_slots) await session.commit() return TimeSlotOut( start_time=new_start_time, end_time=new_end_time, weekdays=new_weekdays, created_at=new_time_slots[0].created_at, updated_at=new_time_slots[0].updated_at, )