안녕하세요. 과제를 잘 못 이해한건가 싶기도 하지만 아래와 같은 조건하에 구현을 해 봤습니다. 수업 과정에서 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, )
안녕하세요. 우선 강의 정말 잘 수강했습니다. https://pub.dev/packages/flutter_seo 해당 커뮤니티를 보다가 댓글에서 해당 패키지를 찾게되었고 강사님도 해당 seo패키지를 사용해서 seo적용을 하신것같은데, 지금 배포된 샐링잇 웹사이트도 해당 seo패키지만을 적용해서 배포를 하신건지 궁금합니다. 그러면 기존 웹사이트 seo만큼은 아니지만, 저 패키지를 적용하면 회사이름(고유명사) 정도는 구글에 치면 나올 정도로는 적용을 할 수 있는건지 궁금합니다.
안녕하세요. 섹션 7의 "35. Nginx, Certbot이 작성한 HTTPS 관련 코드 해석하기" 강의를 들으면서 궁금한 점이 생겨서 질문 남깁니다. server { # 2. 들어온 요청의 주소의 Host가 jscode.p-e.kr일 경우 (ex. http://jscode.p-e.kr/about) # https://jscode.p-e.kr/...(ex. https://jscode.p-e.kr/about)로 리다이렉트(301) 처리 if ($host = jscode.p-e.kr) { return 301 https://$host$request_uri; } # managed by Certbot # 1. jscode.p-e.kr 주소로 들어온 요청이면서 # 80번 포트(http)로 들어오는 요청일 때 # 이 server 블럭에서 처리하도록 설정 listen 80; server_name jscode.p-e.kr; # 3. 그 이외의 경우에는 404(Not Found)로 응답 return 404; # managed by Certbot } 강의자료에서 제공해주시는 /etc/nginx/conf.d/default.conf 파일의 일부 코드입니다. 이미 server_name에서 jscode.p-e.kr 주소로 들어온 요청인지 확인하니깐, if( $host = jscode.p-e.kr ) 는 없어도 되는거 아닌가요? 코드가 존재하는 다른 의도가 있는건지 궁금합니다.
안녕하세요!! 클라우드 관련 공부하다가 go에 관심이 생겨 수강하게 되었습니다. 백엔드는 이 강의와 책으로 처음 공부하는 중입니다. 현재는 강의 프로젝트 구조로 개인 프로젝트 진행해보려고 리펙터링 중 입니다. 수업과 관련은 없지만 3가지 질문이 있습니다. 1. 트랜잭션 구현을 위한 *sql.DB는 어디서 생성하고 어떻게 service에 주입해야 하나요? reposiory는 수정해서, *sql.DB 필드 하나만 위치하도록 했습니다. 다만, 트랜잭션 구현을 하다보니, db 커넥션 풀을 repository에서 service로 가져오거나, cmd에서부터 service까지 내려보내주어야 하더라고요. cmd에 db 커넥션 풀을 생성하도록 하면 안되는 걸까요? orm 도입하기 이전에 내장 패키지로 기본 로직을 구현하려는 건 쓸 데 없는 일일까요? 2. 프로젝트 구조를 어째서 cmd에 전부 주입하는 식으로 작성하셨나요? 테스트는 어떻게 하시나요? cmd를 main패키지 처럼 사용하시는 건지 궁금합니다. 제가 알고 있던 백엔드 구조(spring boot)와 많이 달라 보이네요. 또, 이 구조에서 테스트는 어떻게 진행해야 할지 잘 모르겠습니다. 대략적인 흐름이라도 알고자 여쭤봅니다. 3. 일하실 때에도 이 구조를 유지하시나요? 도메인별 패키지 분리(user, auth, member... etc)는 별로인가요? 개인적으로는 비즈니스 도메인별로 디렉터리를 나누는 것을 선호합니다. 다만, 현재 이 디렉터리 구조를 뜯어고칠 용기가 생기지는 않네요... 이 구조의 어떤 점 때문에 이렇게 사용하시는 지, 혹은 팀에서 이렇게 쓰길래 유지를 하고 계신건지 궁금합니다. 장단점이 있을까요? 궁금한걸 쌓아두면서 gpt로 해결하려다가, 쌓아둔게 터진 것처럼 장황하네요.. 강의를 들으면서 해결 못한 내용이라 그런 것 같아요. 좋은 강의 잘 듣고 있습니다 🥹 🙏
- is_host를 기준으로 호스트 사용자와 게스트 사용자를 구분하는 것은, 가입 당시부터 본인의 역할을 선택하여 구분 짓는 경우에 효과적인 것으로 판단됩니다. 가입 시점부터 역할(게스트/호스트)을 구분 지어서 관리하고자 하는 경우에 적절한 구조라고 생각되며, 호스트의 자격조건이 있거나, 검증이 필요한 서비스라면 이렇게 관리하는 방법이 적절해 보입니다. - 반면, 호스트/게스트의 타입을 캘린더의 존재 여부(또는 갯수)로 정의한다고 하면, 모든 가입자가 가입시에 동일한 자격을 갖는 가입자로서 가입처리가 되고, 추가 단계로서 캘린더를 생성 함으로 써, 호스트와 게스트의 역할을 자유롭게 넘나드는 자유도가 생기는 구조가 될 것 같습니다. - 지금은 커피챗을 목적으로 일정을 조율하는 목적을 갖는 시스템이므로, is_host를 사용하지 않는 결정을 하겠습니다. 전자상거래 시스템같은 판매자와/소비자 처럼 엄격한 구분과 자격을 검증해야 하는 시스템으로 여겨지지는 않기때문입니다. is_host 필드가 없어지는 경우에, 사용자의 상태(또는 역할)을 구분하기 위해서 매번 캘린더의 존재 여부를 확인해야 하지만 앞서 언급한 호스트와 게스트의 역할 변경에 열려있는 점이 중요하게 생각되기 때문입니다. 하지만 모든 가입자가 게스트로 시작하는 만큼 커피챗 호스트를 어떻게 유치할 것인가는 고민이 되는 부분입니다.
CodeDeploy 를 클릭했더니, 이런 문구가 뜨네요. complete your AWS registration 눌러도 콘솔 홈으로 돌아가고, credit card information 을 눌렀더니, 결제 기본 설정 창이 뜨는데, 딱히 문제될 건 없어 보여요. 다른 AWS 서비스 사용할 때는 이런 문구 안 떴는데 왜 CodeDeploy만 이럴까요? ㅠㅠ
책에서는 128p if TYPE_CHECKING: from appserver.apps.calendar.models import Calendar 이렇게 appserver 부터 시작하니다 130페이지 하단에 보면 https://gilbut.co/c/25069573YP 커밋 참조하라고 되어있는데요 깃헙에서는 if TYPE_CHECKING: from apps.calendar.models import Calendar 여기는 apps부터 시작합니다. 초보에게는 이런거 하나하나가 어렵네요 어떤게 맞는 건지 궁금합니다.
인증서 발급 시, 저 빨간 박스에는 "내가 미리 만든 도메인" 이름을 적어야 하는 것 맞나요? Route53에는 아무런 도메인도 안 만든 상태이고, 그래서 레코드 생성도 못해요. 그런데 도메인은 Route53에서 만드는 데 무료 플랜에선 불가능하잖아요. 그래서 다음과 같이 내도메인.한국 사이트에서 커스텀 도메인과 HTTPS 인증서를 만들었어요. 그리고 커스텀 도메인 접속도 성공했어요. 그런데, 분명 인증서는 미국 동부(버지니아 북부) 에서 발급 받아야 하는데, 저는 그런 설정 없이 성공했는데 그 이유가 무엇인가요? 아무런 문제 없나요? 그리고 제가 잘못 알고 있는 부분이 있을까요?